Updated delete logic for voice works.
This commit is contained in:
@@ -53,6 +53,23 @@ public static class WebApplicationExtensions
|
||||
}
|
||||
});
|
||||
|
||||
app.UseExceptionHandler(errorApp =>
|
||||
{
|
||||
errorApp.Run(async context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
context.Response.ContentType = "application/problem+json";
|
||||
|
||||
await Results.Problem(
|
||||
title: "An unexpected error occurred.",
|
||||
detail: app.Environment.IsDevelopment()
|
||||
? "Check the API logs for details."
|
||||
: null,
|
||||
statusCode: StatusCodes.Status500InternalServerError
|
||||
).ExecuteAsync(context);
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
namespace JSMR.Application.VoiceWorks.Commands.Delete;
|
||||
|
||||
public sealed record DeleteVoiceWorkResponse(Dictionary<int, bool> IsSuccess);
|
||||
public sealed record DeleteVoiceWorkResponse(Dictionary<int, DeleteVoiceWorkStatus> Results);
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace JSMR.Application.VoiceWorks.Commands.Delete;
|
||||
|
||||
public enum DeleteVoiceWorkStatus
|
||||
{
|
||||
Deleted,
|
||||
NotFound,
|
||||
NotAllowed,
|
||||
Failed
|
||||
}
|
||||
@@ -20,28 +20,39 @@ public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
|
||||
|
||||
public async Task<DeleteVoiceWorkResponse> DeleteAsync(DeleteVoiceWorkRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
Dictionary<int, bool> isSuccess = [];
|
||||
Dictionary<int, DeleteVoiceWorkStatus> results = request.VoiceWorkIds.Select(x => x)
|
||||
.ToDictionary(x => x, x => DeleteVoiceWorkStatus.NotFound);
|
||||
|
||||
VoiceWork[] voiceWorks = [.. dbContext.VoiceWorks.Where(voiceWork => request.VoiceWorkIds.Contains(voiceWork.VoiceWorkId))
|
||||
.Include(x => x.Circle)];
|
||||
|
||||
foreach (VoiceWork voiceWork in voiceWorks)
|
||||
{
|
||||
isSuccess.Add(voiceWork.VoiceWorkId, false);
|
||||
|
||||
if (voiceWork.Circle is null)
|
||||
if (results.ContainsKey(voiceWork.VoiceWorkId) == false)
|
||||
{
|
||||
results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.NotFound;
|
||||
continue;
|
||||
|
||||
if (voiceWork.IsValid == true && voiceWork.Circle.Spam == false)
|
||||
continue;
|
||||
|
||||
dbContext.Remove(voiceWork);
|
||||
isSuccess[voiceWork.VoiceWorkId] = true;
|
||||
}
|
||||
|
||||
dbContext.SaveChanges();
|
||||
if (voiceWork.Circle is null)
|
||||
{
|
||||
results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.NotFound;
|
||||
continue;
|
||||
}
|
||||
|
||||
return new DeleteVoiceWorkResponse(isSuccess);
|
||||
if (voiceWork.IsValid == true && voiceWork.Circle.Spam == false)
|
||||
{
|
||||
results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.NotAllowed;
|
||||
continue;
|
||||
}
|
||||
|
||||
dbContext.Remove(voiceWork);
|
||||
results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.Deleted;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new DeleteVoiceWorkResponse(results);
|
||||
}
|
||||
|
||||
private async Task<VoiceWork> GetVoiceWorkAsync(int voiceWorkId, CancellationToken cancellationToken)
|
||||
|
||||
@@ -18,9 +18,9 @@ public class Delete_Voice_Work_Tests(MariaDbContainerFixture container) : VoiceW
|
||||
DeleteVoiceWorkRequest request = new([voiceWorkId]);
|
||||
|
||||
DeleteVoiceWorkResponse response = await writer.DeleteAsync(request, TestContext.Current.CancellationToken);
|
||||
response.IsSuccess.Count.ShouldBe(1);
|
||||
response.IsSuccess.ShouldContainKey(voiceWorkId);
|
||||
response.IsSuccess[voiceWorkId].ShouldBeTrue();
|
||||
response.Results.Count.ShouldBe(1);
|
||||
response.Results.ShouldContainKey(voiceWorkId);
|
||||
response.Results[voiceWorkId].ShouldBe(DeleteVoiceWorkStatus.Deleted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -33,8 +33,8 @@ public class Delete_Voice_Work_Tests(MariaDbContainerFixture container) : VoiceW
|
||||
DeleteVoiceWorkRequest request = new([voiceWorkId]);
|
||||
|
||||
DeleteVoiceWorkResponse response = await writer.DeleteAsync(request, TestContext.Current.CancellationToken);
|
||||
response.IsSuccess.Count.ShouldBe(1);
|
||||
response.IsSuccess.ShouldContainKey(voiceWorkId);
|
||||
response.IsSuccess[voiceWorkId].ShouldBeFalse();
|
||||
response.Results.Count.ShouldBe(1);
|
||||
response.Results.ShouldContainKey(voiceWorkId);
|
||||
response.Results[voiceWorkId].ShouldBe(DeleteVoiceWorkStatus.NotAllowed);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@using AntDesign
|
||||
@using JSMR.Application.VoiceWorks.Commands.Delete
|
||||
@using JSMR.Application.VoiceWorks.Commands.SetFavorite
|
||||
@using JSMR.Application.VoiceWorks.Queries.Search
|
||||
@using JSMR.Domain.Enums
|
||||
@@ -136,6 +137,9 @@
|
||||
[Parameter]
|
||||
public required VoiceWorkSearchResult Product { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback ProductDeleted { get; set; }
|
||||
|
||||
private string GetCardClasses(VoiceWorkSearchResult voiceWork)
|
||||
{
|
||||
List<string> classNames = ["j-card", "j-voice-work-card"];
|
||||
@@ -240,7 +244,38 @@
|
||||
{
|
||||
Title = "Are you sure you want to delete the following product?",
|
||||
Icon = icon,
|
||||
Content = Product.ProductName
|
||||
Content = Product.ProductName,
|
||||
Centered = true,
|
||||
OnOk = async (e) =>
|
||||
{
|
||||
DeleteVoiceWorkRequest request = new(
|
||||
VoiceWorkIds: [Product.VoiceWorkId]
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
DeleteVoiceWorkResponse? response = await Client.DeleteVoiceWorkAsync(request);
|
||||
|
||||
if (response is null || response.Results[Product.VoiceWorkId] != DeleteVoiceWorkStatus.Deleted)
|
||||
return;
|
||||
|
||||
await ProductDeleted.InvokeAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AntDesign.ConfirmOptions errorOptions = new()
|
||||
{
|
||||
Title = "Unable to delete product",
|
||||
Content = "Something went wrong while deleting this product. The product was not deleted. Check the API logs for details.",
|
||||
Centered = true,
|
||||
//Width = "70vw",
|
||||
};
|
||||
|
||||
await ModalService.ErrorAsync(errorOptions);
|
||||
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await ModalService.ConfirmAsync(options);
|
||||
|
||||
@@ -13,7 +13,7 @@ else
|
||||
<div class="j-product-items-container">
|
||||
@foreach (var product in Products)
|
||||
{
|
||||
<JProduct Product="@product"></JProduct>
|
||||
<JProduct Product="@product" ProductDeleted="OnProductDeleted"></JProduct>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -21,4 +21,12 @@ else
|
||||
@code {
|
||||
[Parameter]
|
||||
public VoiceWorkSearchResult[]? Products { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback ProductDeleted { get; set; }
|
||||
|
||||
private async Task OnProductDeleted()
|
||||
{
|
||||
await ProductDeleted.InvokeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
9
JSMR.UI.Blazor/Exceptions/ApiException.cs
Normal file
9
JSMR.UI.Blazor/Exceptions/ApiException.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Net;
|
||||
|
||||
namespace JSMR.UI.Blazor.Exceptions;
|
||||
|
||||
public sealed class ApiException(HttpStatusCode statusCode, string message, string? responseBody = null) : Exception(message)
|
||||
{
|
||||
public HttpStatusCode StatusCode { get; } = statusCode;
|
||||
public string? ResponseBody { get; } = responseBody;
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
<JProductCollection Products="upcomingVoiceWorks"></JProductCollection>
|
||||
</BitPivotItem>
|
||||
<BitPivotItem HeaderText="@($"Announcements ({announcedVoiceWorks?.Length ?? 0})")">
|
||||
<JProductCollection Products="announcedVoiceWorks"></JProductCollection>
|
||||
<JProductCollection Products="announcedVoiceWorks" ProductDeleted="OnAnnouncedProductDeleted"></JProductCollection>
|
||||
</BitPivotItem>
|
||||
</BitPivot>
|
||||
|
||||
@@ -105,4 +105,9 @@
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task OnAnnouncedProductDeleted()
|
||||
{
|
||||
_ = LoadAnnouncedVoiceWorksAsync();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using JSMR.Application.Tags.Queries.Search;
|
||||
using JSMR.Application.VoiceWorks.Commands.Delete;
|
||||
using JSMR.Application.VoiceWorks.Commands.SetFavorite;
|
||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||
using JSMR.UI.Blazor.Exceptions;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -23,6 +24,18 @@ public class VoiceWorksClient(HttpClient http)
|
||||
}
|
||||
};
|
||||
|
||||
private static async Task<T?> ReadJsonOrThrowAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
string body = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
throw new ApiException(response.StatusCode, $"Request failed: {(int)response.StatusCode}", body);
|
||||
}
|
||||
|
||||
return await response.Content.ReadFromJsonAsync<T>(JsonOptions, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<SearchVoiceWorksResponse?> SearchAsync(SearchVoiceWorksRequest request, CancellationToken ct = default)
|
||||
{
|
||||
using var resp = await http.PostAsJsonAsync("/api/voiceworks/search", request, ct);
|
||||
@@ -56,7 +69,8 @@ public class VoiceWorksClient(HttpClient http)
|
||||
public async Task<DeleteVoiceWorkResponse?> DeleteVoiceWorkAsync(DeleteVoiceWorkRequest request, CancellationToken ct = default)
|
||||
{
|
||||
using var resp = await http.PostAsJsonAsync("/api/voicework/delete", request, ct);
|
||||
return await resp.Content.ReadFromJsonAsync<DeleteVoiceWorkResponse>(JsonOptions, cancellationToken: ct);
|
||||
//return await resp.Content.ReadFromJsonAsync<DeleteVoiceWorkResponse>(JsonOptions, cancellationToken: ct);
|
||||
return await ReadJsonOrThrowAsync<DeleteVoiceWorkResponse>(resp, ct);
|
||||
}
|
||||
|
||||
public async Task<UpdateTagStatusResponse?> UpdateTagStatusAsync(UpdateTagStatusRequest request, CancellationToken ct = default)
|
||||
|
||||
Reference in New Issue
Block a user