diff --git a/Backend/src/Modules/Files/Files.Api/Endpoints/Files/DeleteFileAsset.cs b/Backend/src/Modules/Files/Files.Api/Endpoints/Files/DeleteFileAsset.cs new file mode 100644 index 00000000..1dab9797 --- /dev/null +++ b/Backend/src/Modules/Files/Files.Api/Endpoints/Files/DeleteFileAsset.cs @@ -0,0 +1,25 @@ +using Files.Application.Entities.Files.Commands.DeleteFileAsset; + +namespace Files.Api.Endpoints.Files; + +public class DeleteFileAsset : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + app.MapDelete("/Files/{fileId}", async (string fileId, ISender sender) => + { + var result = await sender.Send(new DeleteFileAssetCommand(fileId)); + var response = result.Adapt(); + + return Results.Ok(response); + }) + .WithName("DeleteFileAsset") + .Produces() + .ProducesProblem(StatusCodes.Status400BadRequest) + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Delete File Asset") + .WithDescription("Delete File Asset"); + } +} + +public record DeleteFileAssetResponse(bool FileDeleted); diff --git a/Backend/src/Modules/Files/Files.Application/Entities/Files/Commands/DeleteFileAsset/DeleteFileAssetCommand.cs b/Backend/src/Modules/Files/Files.Application/Entities/Files/Commands/DeleteFileAsset/DeleteFileAssetCommand.cs new file mode 100644 index 00000000..cce4416e --- /dev/null +++ b/Backend/src/Modules/Files/Files.Application/Entities/Files/Commands/DeleteFileAsset/DeleteFileAssetCommand.cs @@ -0,0 +1,18 @@ +namespace Files.Application.Entities.Files.Commands.DeleteFileAsset; + +public record DeleteFileAssetCommand(FileAssetId Id) : ICommand +{ + public DeleteFileAssetCommand(string Id) : this(FileAssetId.Of(Guid.Parse(Id))) { } +} + +public record DeleteFileAssetResult(bool FileDeleted); + +public class DeleteFileAssetCommandValidator : AbstractValidator +{ + public DeleteFileAssetCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Files/Files.Application/Entities/Files/Commands/DeleteFileAsset/DeleteFileAssetHandler.cs b/Backend/src/Modules/Files/Files.Application/Entities/Files/Commands/DeleteFileAsset/DeleteFileAssetHandler.cs new file mode 100644 index 00000000..183d6b87 --- /dev/null +++ b/Backend/src/Modules/Files/Files.Application/Entities/Files/Commands/DeleteFileAsset/DeleteFileAssetHandler.cs @@ -0,0 +1,21 @@ +using Files.Application.Entities.Files.Exceptions; + +namespace Files.Application.Entities.Files.Commands.DeleteFileAsset; + +public class DeleteFileAssetHandler(IFilesDbContext dbContext) : ICommandHandler +{ + public async Task Handle(DeleteFileAssetCommand command, CancellationToken cancellationToken) + { + var fileAsset = await dbContext.FileAssets + .AsNoTracking() + .SingleOrDefaultAsync(f => f.Id == command.Id, cancellationToken); + + if (fileAsset is null) + throw new FileAssetNotFoundException(command.Id.ToString()); + + dbContext.FileAssets.Remove(fileAsset); + await dbContext.SaveChangesAsync(cancellationToken); + + return new DeleteFileAssetResult(true); + } +} diff --git a/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdHandler.cs b/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdHandler.cs index 52a584b8..dff01157 100644 --- a/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdHandler.cs +++ b/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdHandler.cs @@ -9,10 +9,10 @@ public async Task Handle(GetFileAssetByIdQuery query, Ca { var fileAsset = await dbContext.FileAssets .AsNoTracking() - .SingleOrDefaultAsync(f => f.Id == FileAssetId.Of(Guid.Parse(query.Id)), cancellationToken); + .SingleOrDefaultAsync(f => f.Id == query.Id, cancellationToken); if (fileAsset is null) - throw new FileAssetNotFoundException(query.Id); + throw new FileAssetNotFoundException(query.Id.ToString()); return new GetFileAssetByIdResult(fileAsset.ToFileAssetDto()); } diff --git a/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdQuery.cs b/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdQuery.cs index 53bcf999..7f8ca5aa 100644 --- a/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdQuery.cs +++ b/Backend/src/Modules/Files/Files.Application/Entities/Files/Queries/GetFileAssetById/GetFileAssetByIdQuery.cs @@ -4,7 +4,21 @@ namespace Files.Application.Entities.Files.Queries.GetFileAssetById; -public record GetFileAssetByIdQuery(string Id) : IQuery; +public record GetFileAssetByIdQuery(FileAssetId Id) : IQuery +{ + public GetFileAssetByIdQuery(string Id) : this(FileAssetId.Of(Guid.Parse(Id))) { } +} // ReSharper disable once NotAccessedPositionalProperty.Global + public record GetFileAssetByIdResult(FileAssetDto FileAssetDto); + +public class GetFileAssetByIdQueryValidator : AbstractValidator +{ + public GetFileAssetByIdQueryValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Nodes/Nodes.Api/Endpoints/Nodes/DeleteNode.cs b/Backend/src/Modules/Nodes/Nodes.Api/Endpoints/Nodes/DeleteNode.cs new file mode 100644 index 00000000..cc09ee3f --- /dev/null +++ b/Backend/src/Modules/Nodes/Nodes.Api/Endpoints/Nodes/DeleteNode.cs @@ -0,0 +1,25 @@ +using Nodes.Application.Entities.Nodes.Commands.DeleteNode; + +namespace Nodes.Api.Endpoints.Nodes; + +public class DeleteNode : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + app.MapDelete("/Nodes/{nodeId}", async (string nodeId, ISender sender) => + { + var result = await sender.Send(new DeleteNodeCommand(nodeId)); + var response = result.Adapt(); + + return Results.Ok(response); + }) + .WithName("DeleteNode") + .Produces() + .ProducesProblem(StatusCodes.Status400BadRequest) + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Delete Node") + .WithDescription("Delete Node"); + } +} + +public record DeleteNodeResponse(bool NodeDeleted); diff --git a/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Commands/DeleteNode/DeleteNodeCommand.cs b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Commands/DeleteNode/DeleteNodeCommand.cs new file mode 100644 index 00000000..645c4cb5 --- /dev/null +++ b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Commands/DeleteNode/DeleteNodeCommand.cs @@ -0,0 +1,18 @@ +namespace Nodes.Application.Entities.Nodes.Commands.DeleteNode; + +public record DeleteNodeCommand(NodeId Id) : ICommand +{ + public DeleteNodeCommand(string Id) : this(NodeId.Of(Guid.Parse(Id))) { } +} + +public record DeleteNodeResult(bool NodeDeleted); + +public class DeleteNodeCommandValidator : AbstractValidator +{ + public DeleteNodeCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Commands/DeleteNode/DeleteNodeHandler.cs b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Commands/DeleteNode/DeleteNodeHandler.cs new file mode 100644 index 00000000..1106ae30 --- /dev/null +++ b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Commands/DeleteNode/DeleteNodeHandler.cs @@ -0,0 +1,21 @@ +using Nodes.Application.Entities.Nodes.Exceptions; + +namespace Nodes.Application.Entities.Nodes.Commands.DeleteNode; + +public class DeleteNodeHandler(INodesDbContext dbContext) : ICommandHandler +{ + public async Task Handle(DeleteNodeCommand command, CancellationToken cancellationToken) + { + var node = await dbContext.Nodes + .AsNoTracking() + .SingleOrDefaultAsync(n => n.Id == command.Id, cancellationToken); + + if (node is null) + throw new NodeNotFoundException(command.Id.ToString()); + + dbContext.Nodes.Remove(node); + await dbContext.SaveChangesAsync(cancellationToken); + + return new DeleteNodeResult(true); + } +} diff --git a/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdHandler.cs b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdHandler.cs index 279532a7..ee239dac 100644 --- a/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdHandler.cs +++ b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdHandler.cs @@ -7,14 +7,12 @@ internal class GetNodeByIdHandler(INodesDbContext dbContext) : IQueryHandler Handle(GetNodeByIdQuery query, CancellationToken cancellationToken) { - var nodeId = query.Id.ToString(); - var node = await dbContext.Nodes .AsNoTracking() - .SingleOrDefaultAsync(n => n.Id == NodeId.Of(Guid.Parse(nodeId)), cancellationToken); + .SingleOrDefaultAsync(n => n.Id == query.Id, cancellationToken); if (node is null) - throw new NodeNotFoundException(nodeId); + throw new NodeNotFoundException(query.Id.ToString()); return new GetNodeByIdResult(node.ToNodeDto()); } diff --git a/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdQuery.cs b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdQuery.cs index 5a3b6e64..289a7365 100644 --- a/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdQuery.cs +++ b/Backend/src/Modules/Nodes/Nodes.Application/Entities/Nodes/Queries/GetNodeById/GetNodeByIdQuery.cs @@ -10,6 +10,7 @@ public GetNodeByIdQuery(string Id) : this(NodeId.Of(Guid.Parse(Id))) { } } // ReSharper disable once NotAccessedPositionalProperty.Global + public record GetNodeByIdResult(NodeDto NodeDto); public class GetNodeByIdQueryValidator : AbstractValidator diff --git a/Backend/src/Modules/Notes/Notes.Api/Endpoints/Notes/DeleteNote.cs b/Backend/src/Modules/Notes/Notes.Api/Endpoints/Notes/DeleteNote.cs new file mode 100644 index 00000000..70240adc --- /dev/null +++ b/Backend/src/Modules/Notes/Notes.Api/Endpoints/Notes/DeleteNote.cs @@ -0,0 +1,25 @@ +using Notes.Application.Entities.Notes.Commands.DeleteNote; + +namespace Notes.Api.Endpoints.Notes; + +public class DeleteNote : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + app.MapDelete("/Notes/{noteId}", async (string noteId, ISender sender) => + { + var result = await sender.Send(new DeleteNoteCommand(noteId)); + var response = result.Adapt(); + + return Results.Ok(response); + }) + .WithName("DeleteNote") + .Produces() + .ProducesProblem(StatusCodes.Status400BadRequest) + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Delete Note") + .WithDescription("Delete Note"); + } +} + +public record DeleteNoteResponse(bool NoteDeleted); diff --git a/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Commands/DeleteNote/DeleteNoteCommand.cs b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Commands/DeleteNote/DeleteNoteCommand.cs new file mode 100644 index 00000000..8cac44d4 --- /dev/null +++ b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Commands/DeleteNote/DeleteNoteCommand.cs @@ -0,0 +1,18 @@ +namespace Notes.Application.Entities.Notes.Commands.DeleteNote; + +public record DeleteNoteCommand(NoteId Id) : ICommand +{ + public DeleteNoteCommand(string Id) : this(NoteId.Of(Guid.Parse(Id))) { } +} + +public record DeleteNoteResult(bool NoteDeleted); + +public class DeleteNoteCommandValidator : AbstractValidator +{ + public DeleteNoteCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Commands/DeleteNote/DeleteNoteHandler.cs b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Commands/DeleteNote/DeleteNoteHandler.cs new file mode 100644 index 00000000..d281bfdc --- /dev/null +++ b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Commands/DeleteNote/DeleteNoteHandler.cs @@ -0,0 +1,21 @@ +using Notes.Application.Entities.Notes.Exceptions; + +namespace Notes.Application.Entities.Notes.Commands.DeleteNote; + +public class DeleteNoteHandler(INotesDbContext dbContext) : ICommandHandler +{ + public async Task Handle(DeleteNoteCommand command, CancellationToken cancellationToken) + { + var note = await dbContext.Notes + .AsNoTracking() + .SingleOrDefaultAsync(n => n.Id == command.Id, cancellationToken); + + if (note is null) + throw new NoteNotFoundException(command.Id.ToString()); + + dbContext.Notes.Remove(note); + await dbContext.SaveChangesAsync(cancellationToken); + + return new DeleteNoteResult(true); + } +} diff --git a/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdHandler.cs b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdHandler.cs index a98d6481..af1f6df7 100644 --- a/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdHandler.cs +++ b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdHandler.cs @@ -9,10 +9,10 @@ public async Task Handle(GetNoteByIdQuery query, Cancellation { var note = await dbContext.Notes .AsNoTracking() - .SingleOrDefaultAsync(n => n.Id == NoteId.Of(Guid.Parse(query.Id)), cancellationToken); + .SingleOrDefaultAsync(n => n.Id == query.Id, cancellationToken); if (note is null) - throw new NoteNotFoundException(query.Id); + throw new NoteNotFoundException(query.Id.ToString()); return new GetNoteByIdResult(note.ToNoteDto()); } diff --git a/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdQuery.cs b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdQuery.cs index 7d7c8be0..61aa4d0b 100644 --- a/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdQuery.cs +++ b/Backend/src/Modules/Notes/Notes.Application/Entities/Notes/Queries/GetNoteById/GetNoteByIdQuery.cs @@ -1,7 +1,24 @@ -using Notes.Application.Entities.Notes.Dtos; +// ReSharper disable ClassNeverInstantiated.Global + +using Notes.Application.Entities.Notes.Dtos; namespace Notes.Application.Entities.Notes.Queries.GetNoteById; -public record GetNoteByIdQuery(string Id) : IQuery; +public record GetNoteByIdQuery(NoteId Id) : IQuery +{ + public GetNoteByIdQuery(string Id) : this(NoteId.Of(Guid.Parse(Id))) { } +} + +// ReSharper disable once NotAccessedPositionalProperty.Global public record GetNoteByIdResult(NoteDto NoteDto); + +public class GetNoteByIdQueryValidator : AbstractValidator +{ + public GetNoteByIdQueryValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Reminders/Reminders.Api/Endpoints/Reminders/DeleteReminder.cs b/Backend/src/Modules/Reminders/Reminders.Api/Endpoints/Reminders/DeleteReminder.cs new file mode 100644 index 00000000..598ac5d6 --- /dev/null +++ b/Backend/src/Modules/Reminders/Reminders.Api/Endpoints/Reminders/DeleteReminder.cs @@ -0,0 +1,25 @@ +using Reminders.Application.Entities.Reminders.Commands.DeleteReminder; + +namespace Reminders.Api.Endpoints.Reminders; + +public class DeleteReminder : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + app.MapDelete("/Reminders/{reminderId}", async (string reminderId, ISender sender) => + { + var result = await sender.Send(new DeleteReminderCommand(reminderId)); + var response = result.Adapt(); + + return Results.Ok(response); + }) + .WithName("DeleteReminder") + .Produces() + .ProducesProblem(StatusCodes.Status400BadRequest) + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Delete Reminder") + .WithDescription("Delete Reminder"); + } +} + +public record DeleteReminderResponse(bool ReminderDeleted); diff --git a/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Commands/DeleteReminder/DeleteReminderCommand.cs b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Commands/DeleteReminder/DeleteReminderCommand.cs new file mode 100644 index 00000000..5d22e5c8 --- /dev/null +++ b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Commands/DeleteReminder/DeleteReminderCommand.cs @@ -0,0 +1,18 @@ +namespace Reminders.Application.Entities.Reminders.Commands.DeleteReminder; + +public record DeleteReminderCommand(ReminderId Id) : ICommand +{ + public DeleteReminderCommand(string Id) : this(ReminderId.Of(Guid.Parse(Id))) { } +} + +public record DeleteReminderResult(bool ReminderDeleted); + +public class DeleteReminderCommandValidator : AbstractValidator +{ + public DeleteReminderCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Commands/DeleteReminder/DeleteReminderHandler.cs b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Commands/DeleteReminder/DeleteReminderHandler.cs new file mode 100644 index 00000000..24ef3236 --- /dev/null +++ b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Commands/DeleteReminder/DeleteReminderHandler.cs @@ -0,0 +1,21 @@ +using Reminders.Application.Entities.Reminders.Exceptions; + +namespace Reminders.Application.Entities.Reminders.Commands.DeleteReminder; + +public class DeleteReminderHandler(IRemindersDbContext dbContext) : ICommandHandler +{ + public async Task Handle(DeleteReminderCommand command, CancellationToken cancellationToken) + { + var reminder = await dbContext.Reminders + .AsNoTracking() + .SingleOrDefaultAsync(r => r.Id == command.Id, cancellationToken); + + if (reminder is null) + throw new ReminderNotFoundException(command.Id.ToString()); + + dbContext.Reminders.Remove(reminder); + await dbContext.SaveChangesAsync(cancellationToken); + + return new DeleteReminderResult(true); + } +} diff --git a/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdHandler.cs b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdHandler.cs index 40891726..321d50d3 100644 --- a/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdHandler.cs +++ b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdHandler.cs @@ -9,10 +9,10 @@ public async Task Handle(GetReminderByIdQuery query, Canc { var reminder = await dbContext.Reminders .AsNoTracking() - .SingleOrDefaultAsync(r => r.Id == ReminderId.Of(Guid.Parse(query.Id)), cancellationToken); + .SingleOrDefaultAsync(r => r.Id == query.Id, cancellationToken); if (reminder is null) - throw new ReminderNotFoundException(query.Id); + throw new ReminderNotFoundException(query.Id.ToString()); return new GetReminderByIdResult(reminder.ToReminderDto()); } diff --git a/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdQuery.cs b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdQuery.cs index 8be8cc8a..cc70f762 100644 --- a/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdQuery.cs +++ b/Backend/src/Modules/Reminders/Reminders.Application/Entities/Reminders/Queries/GetReminderById/GetReminderByIdQuery.cs @@ -4,7 +4,21 @@ namespace Reminders.Application.Entities.Reminders.Queries.GetReminderById; -public record GetReminderByIdQuery(string Id) : IQuery; +public record GetReminderByIdQuery(ReminderId Id) : IQuery +{ + public GetReminderByIdQuery(string Id) : this(ReminderId.Of(Guid.Parse(Id))) { } +} // ReSharper disable once NotAccessedPositionalProperty.Global + public record GetReminderByIdResult(ReminderDto ReminderDto); + +public class GetReminderByIdQueryValidator : AbstractValidator +{ + public GetReminderByIdQueryValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Timelines/Timelines.Api/Endpoints/Timelines/DeleteTimeline.cs b/Backend/src/Modules/Timelines/Timelines.Api/Endpoints/Timelines/DeleteTimeline.cs new file mode 100644 index 00000000..248fcc48 --- /dev/null +++ b/Backend/src/Modules/Timelines/Timelines.Api/Endpoints/Timelines/DeleteTimeline.cs @@ -0,0 +1,25 @@ +using Timelines.Application.Entities.Timelines.Commands.DeleteTimeline; + +namespace Timelines.Api.Endpoints.Timelines; + +public class DeleteTimeline : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + app.MapDelete("/Timelines/{timelineId}", async (string timelineId, ISender sender) => + { + var result = await sender.Send(new DeleteTimelineCommand(timelineId)); + var response = result.Adapt(); + + return Results.Ok(response); + }) + .WithName("DeleteTimeline") + .Produces() + .ProducesProblem(StatusCodes.Status400BadRequest) + .ProducesProblem(StatusCodes.Status404NotFound) + .WithSummary("Delete Timeline") + .WithDescription("Delete Timeline"); + } +} + +public record DeleteTimelineResponse(bool TimelineDeleted); diff --git a/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Commands/DeleteTimeline/DeleteTimelineCommand.cs b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Commands/DeleteTimeline/DeleteTimelineCommand.cs new file mode 100644 index 00000000..32b78748 --- /dev/null +++ b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Commands/DeleteTimeline/DeleteTimelineCommand.cs @@ -0,0 +1,18 @@ +namespace Timelines.Application.Entities.Timelines.Commands.DeleteTimeline; + +public record DeleteTimelineCommand(TimelineId Id) : ICommand +{ + public DeleteTimelineCommand(string Id) : this(TimelineId.Of(Guid.Parse(Id))) { } +} + +public record DeleteTimelineResult(bool TimelineDeleted); + +public class DeleteTimelineCommandValidator : AbstractValidator +{ + public DeleteTimelineCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +} diff --git a/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Commands/DeleteTimeline/DeleteTimelineHandler.cs b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Commands/DeleteTimeline/DeleteTimelineHandler.cs new file mode 100644 index 00000000..731e7cc8 --- /dev/null +++ b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Commands/DeleteTimeline/DeleteTimelineHandler.cs @@ -0,0 +1,21 @@ +using Timelines.Application.Entities.Timelines.Exceptions; + +namespace Timelines.Application.Entities.Timelines.Commands.DeleteTimeline; + +public class DeleteTimelineHandler(ITimelinesDbContext dbContext) : ICommandHandler +{ + public async Task Handle(DeleteTimelineCommand command, CancellationToken cancellationToken) + { + var timeline = await dbContext.Timelines + .AsNoTracking() + .SingleOrDefaultAsync(t => t.Id == command.Id, cancellationToken); + + if (timeline is null) + throw new TimelineNotFoundException(command.Id.ToString()); + + dbContext.Timelines.Remove(timeline); + await dbContext.SaveChangesAsync(cancellationToken); + + return new DeleteTimelineResult(true); + } +} diff --git a/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdHandler.cs b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdHandler.cs index f31d0b6e..8f9fef90 100644 --- a/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdHandler.cs +++ b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdHandler.cs @@ -9,10 +9,10 @@ public async Task Handle(GetTimelineByIdQuery query, Canc { var timeline = await dbContext.Timelines .AsNoTracking() - .SingleOrDefaultAsync(t => t.Id == TimelineId.Of(Guid.Parse(query.Id)), cancellationToken); + .SingleOrDefaultAsync(t => t.Id == query.Id, cancellationToken); if (timeline is null) - throw new TimelineNotFoundException(query.Id); + throw new TimelineNotFoundException(query.Id.ToString()); return new GetTimelineByIdResult(timeline.ToTimelineDto()); } diff --git a/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdQuery.cs b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdQuery.cs index 9e07fb58..0868f8cd 100644 --- a/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdQuery.cs +++ b/Backend/src/Modules/Timelines/Timelines.Application/Entities/Timelines/Queries/GetTimelineById/GetTimelineByIdQuery.cs @@ -4,7 +4,21 @@ namespace Timelines.Application.Entities.Timelines.Queries.GetTimelineById; -public record GetTimelineByIdQuery(string Id) : IQuery; +public record GetTimelineByIdQuery(TimelineId Id) : IQuery +{ + public GetTimelineByIdQuery(string Id) : this(TimelineId.Of(Guid.Parse(Id))) { } +} // ReSharper disable once NotAccessedPositionalProperty.Global + public record GetTimelineByIdResult(TimelineDto TimelineDto); + +public class GetTimelineByIdQueryValidator : AbstractValidator +{ + public GetTimelineByIdQueryValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .Must(value => Guid.TryParse(value.ToString(), out _)).WithMessage("Id is not valid."); + } +}