From db4b22446b9fa284cfccdb0c20db0084a2c0fbd0 Mon Sep 17 00:00:00 2001 From: Marcos Venicius Date: Sun, 9 Apr 2023 10:25:30 -0300 Subject: [PATCH 1/3] create cli command behaviour to delete a task --- GrpcTodo.CLI/ActionRunner.cs | 8 +- GrpcTodo.CLI/CLI.cs | 10 +++ GrpcTodo.CLI/Menu.cs | 3 +- GrpcTodo.CLI/UseCases/Common/Prompt.cs | 3 + .../UseCases/TaskDelete/TaskDeletePrompt.cs | 56 +++++++++++++ .../UseCases/TaskDelete/TaskDeleteUseCase.cs | 43 ++++++++++ .../UseCases/TaskList/TaskListUseCase.cs | 80 ++++++++----------- 7 files changed, 153 insertions(+), 50 deletions(-) create mode 100644 GrpcTodo.CLI/UseCases/TaskDelete/TaskDeletePrompt.cs create mode 100644 GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs diff --git a/GrpcTodo.CLI/ActionRunner.cs b/GrpcTodo.CLI/ActionRunner.cs index 50140ff..43dd588 100644 --- a/GrpcTodo.CLI/ActionRunner.cs +++ b/GrpcTodo.CLI/ActionRunner.cs @@ -8,6 +8,7 @@ using GrpcTodo.CLI.UseCases.AliasList; using GrpcTodo.CLI.UseCases.AliasRemove; using GrpcTodo.CLI.UseCases.TaskCreate; +using GrpcTodo.CLI.UseCases.TaskDelete; using GrpcTodo.CLI.UseCases.TaskList; namespace GrpcTodo.CLI; @@ -23,6 +24,7 @@ internal class ActionRunner private readonly AliasRemoveUseCase _aliasRemoveUseCase; private readonly TaskCreateUseCase _taskCreateUseCase; private readonly TaskListUseCase _taskListUseCase; + private readonly TaskDeleteUseCase _taskDeleteUseCase; private readonly CommandReader _commandReader; @@ -39,6 +41,7 @@ public ActionRunner(ConfigsManager configsManager, CommandReader commandReader, _aliasRemoveUseCase = new AliasRemoveUseCase(configsManager, commandReader); _taskCreateUseCase = new TaskCreateUseCase(configsManager); _taskListUseCase = new TaskListUseCase(configsManager, parameters); + _taskDeleteUseCase = new TaskDeleteUseCase(configsManager); } public Task Run(Command? action) @@ -65,15 +68,14 @@ public Task Run(Command? action) return _taskCreateUseCase.ExecuteAsync(); case Command.ListAllTasks: return _taskListUseCase.ExecuteAsync(); + case Command.DeleteTask: + return _taskDeleteUseCase.ExecuteAsync(); case Command.CompleteTask: Console.WriteLine("no implemented yet: complete task"); break; case Command.UncompleteTask: Console.WriteLine("no implemented yet: uncomplete task"); break; - case Command.DeleteTask: - Console.WriteLine("no implemented yet: delete task"); - break; } return Task.CompletedTask; diff --git a/GrpcTodo.CLI/CLI.cs b/GrpcTodo.CLI/CLI.cs index f9f2ce2..87bf988 100644 --- a/GrpcTodo.CLI/CLI.cs +++ b/GrpcTodo.CLI/CLI.cs @@ -1,3 +1,5 @@ +using Grpc.Core; + using GrpcTodo.CLI.Lib; using GrpcTodo.CLI.Models; using GrpcTodo.CLI.Services; @@ -48,6 +50,14 @@ public async Task Run() Menu.ShowAvailableOptions(parameters); } } + catch (RpcException e) + { + ConsoleWritter.WriteError(e.Status.Detail ?? e.Message); + } + catch (ArgumentNullException e) + { + ConsoleWritter.WriteError(e.Message); + } catch (ShowErrorMessageException e) { ConsoleWritter.WriteError(e.Message); diff --git a/GrpcTodo.CLI/Menu.cs b/GrpcTodo.CLI/Menu.cs index 13ca5ba..dcf9622 100644 --- a/GrpcTodo.CLI/Menu.cs +++ b/GrpcTodo.CLI/Menu.cs @@ -99,8 +99,9 @@ public sealed class Menu }, new MenuOption { Path = "delete", + IsImplemented = true, Command = Command.DeleteTask, - Description = "delete a task" + Description = "delete a task. example: gl delete task " }, } } diff --git a/GrpcTodo.CLI/UseCases/Common/Prompt.cs b/GrpcTodo.CLI/UseCases/Common/Prompt.cs index d7b548f..2a6034b 100644 --- a/GrpcTodo.CLI/UseCases/Common/Prompt.cs +++ b/GrpcTodo.CLI/UseCases/Common/Prompt.cs @@ -10,6 +10,9 @@ private protected record PromptOptions public bool RemoveWhitespaces { get; init; } = false; public bool ShouldBeNumber { get; init; } = false; public bool ShouldBeSingleWord { get; init; } = false; + /// + /// returns true when should show the error, false when not + /// public Func? Custom { get; init; } public string? CustomMessage { get; init; } public bool ShouldBeHidden { get; init; } = false; diff --git a/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeletePrompt.cs b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeletePrompt.cs new file mode 100644 index 0000000..e8badc8 --- /dev/null +++ b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeletePrompt.cs @@ -0,0 +1,56 @@ +using System.Text; + +using GrpcTodo.CLI.UseCases.Common; +using GrpcTodo.CLI.Utils; +using GrpcTodo.SharedKernel.Protos.Tasks.Responses; + +namespace GrpcTodo.CLI.UseCases.TaskDelete; + +public sealed class TaskDeletePrompt : Prompt +{ + public string PromptTask(IOrderedEnumerable tasks) + { + Dictionary tasksId = new(); + + foreach (var task in tasks) + tasksId.Add(task.Id[0..4], true); + + ConsoleWritter.WriteInfo("Choose a task id to delete\n"); + + ConsoleWritter.WriteWithColor("+ completed", ConsoleColor.Green); + ConsoleWritter.WriteWithColor("- uncompleted", ConsoleColor.White); + Console.WriteLine(); + + StringBuilder sb = new(); + + foreach (var task in tasks) + { + var id = task.Id[0..4]; + + sb.Append(task.Completed ? "+" : "- "); + + var createdAt = new DateTime(task.CreatedAt); + + sb.Append($"[{id}] [{createdAt:MM/dd HH:mm}] {task.Name}"); + + if (task.Completed) + ConsoleWritter.WriteWithColor(sb.ToString(), ConsoleColor.Green); + else + ConsoleWritter.WriteWithColor(sb.ToString(), ConsoleColor.White); + + sb.Clear(); + } + + Console.WriteLine(); + + var taskId = Read("task id: ", new PromptOptions + { + RemoveWhitespaces = true, + ShouldBeSingleWord = true, + CustomMessage = "This task id does not exists", + Custom = id => !tasksId.ContainsKey(id) + }); + + return taskId; + } +} diff --git a/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs new file mode 100644 index 0000000..733fd0a --- /dev/null +++ b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs @@ -0,0 +1,43 @@ + +using Grpc.Net.Client; + +using GrpcTodo.CLI.Lib; +using GrpcTodo.CLI.UseCases.Common; +using GrpcTodo.CLI.Utils; +using GrpcTodo.SharedKernel.Protos.Tasks; +using GrpcTodo.SharedKernel.Protos.Tasks.Requests; + +namespace GrpcTodo.CLI.UseCases.TaskDelete; +public class TaskDeleteUseCase : UseCase +{ + public TaskDeleteUseCase(ConfigsManager configsManager) : base(configsManager) + { + } + + public override async Task ExecuteAsync() + { + var accessToken = _configsManager.GetItem(ConfigKey.Item, Settings.AuthTokenKey); + + if (accessToken is null) + throw new ArgumentNullException(nameof(accessToken), "you are not authenticated"); + + using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + + var client = new TaskItem.TaskItemClient(channel); + + var request = new TaskListRequest + { + AccessToken = accessToken + }; + + var response = await client.ListAllAsync(request); + + var tasks = response.Items.OrderByDescending(x => x.CreatedAt); + + var taskDeletePrompt = new TaskDeletePrompt(); + + var taskIdToDelete = taskDeletePrompt.PromptTask(tasks); + + ConsoleWritter.Write($"the task [{taskIdToDelete}] will be deleted"); + } +} diff --git a/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs b/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs index 83c2f02..4f740be 100644 --- a/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs +++ b/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs @@ -1,6 +1,5 @@ using System.Text; -using Grpc.Core; using Grpc.Net.Client; using GrpcTodo.CLI.Lib; @@ -22,66 +21,55 @@ public TaskListUseCase(ConfigsManager configsManager, Parameters parameters) : b public override async Task ExecuteAsync() { - try - { - var accessToken = _configsManager.GetItem(ConfigKey.Item, Settings.AuthTokenKey); + var accessToken = _configsManager.GetItem(ConfigKey.Item, Settings.AuthTokenKey); - if (accessToken is null) - throw new ArgumentNullException(nameof(accessToken), "you are not authenticated"); + if (accessToken is null) + throw new ArgumentNullException(nameof(accessToken), "you are not authenticated"); - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); - var client = new TaskItem.TaskItemClient(channel); + var client = new TaskItem.TaskItemClient(channel); - var request = new TaskListRequest - { - AccessToken = accessToken - }; + var request = new TaskListRequest + { + AccessToken = accessToken + }; - var response = await client.ListAllAsync(request); + var response = await client.ListAllAsync(request); - var tasks = response.Items.OrderByDescending(x => x.CreatedAt); + var tasks = response.Items.OrderByDescending(x => x.CreatedAt); - ConsoleWritter.WriteInfo("==== ALL TASKS ===\n"); - ConsoleWritter.WriteWithColor("+ completed", ConsoleColor.Green); - ConsoleWritter.WriteWithColor("- uncompleted", ConsoleColor.White); - Console.WriteLine(); + ConsoleWritter.WriteInfo("==== ALL TASKS ===\n"); + ConsoleWritter.WriteWithColor("+ completed", ConsoleColor.Green); + ConsoleWritter.WriteWithColor("- uncompleted", ConsoleColor.White); + Console.WriteLine(); - StringBuilder sb = new(); + StringBuilder sb = new(); - foreach (var task in tasks) - { - var id = task.Id[0..4]; - - sb.Append(task.Completed ? "+" : "- "); + foreach (var task in tasks) + { + var id = task.Id[0..4]; - if (_parameters.Has("--full")) - { - var createdAt = new DateTime(task.CreatedAt); + sb.Append(task.Completed ? "+" : "- "); - sb.Append($"[{id}] "); - sb.Append($"[{createdAt:MM/dd HH:mm}] "); - } + if (_parameters.Has("--full")) + { + var createdAt = new DateTime(task.CreatedAt); - sb.Append(task.Name); + sb.Append($"[{id}] "); + sb.Append($"[{createdAt:MM/dd HH:mm}] "); + } - if (task.Completed) - ConsoleWritter.WriteWithColor(sb.ToString(), ConsoleColor.Green); - else - ConsoleWritter.WriteWithColor(sb.ToString(), ConsoleColor.White); + sb.Append(task.Name); - sb.Clear(); - } + if (task.Completed) + ConsoleWritter.WriteWithColor(sb.ToString(), ConsoleColor.Green); + else + ConsoleWritter.WriteWithColor(sb.ToString(), ConsoleColor.White); - Console.WriteLine(); - } - catch (RpcException e) - { - ConsoleWritter.WriteError(e.Status.Detail ?? e.Message); - } - catch (ArgumentNullException e) - { - ConsoleWritter.WriteError(e.Message); + sb.Clear(); } + + Console.WriteLine(); } } From c98b2c771c59691a333355da7d115feede964ae9 Mon Sep 17 00:00:00 2001 From: Marcos Venicus Date: Mon, 5 Jun 2023 19:15:55 -0300 Subject: [PATCH 2/3] allow http connection --- .../AccountCreate/AccountCreateUseCase.cs | 3 +-- .../AccountLogin/AccountLoginUseCase.cs | 3 +-- .../AccountTokenUpdateUseCase.cs | 3 +-- .../UseCases/TaskCreate/TaskCreateUseCase.cs | 3 +-- .../UseCases/TaskDelete/TaskDeleteUseCase.cs | 7 ++----- .../UseCases/TaskList/TaskListUseCase.cs | 4 +--- GrpcTodo.CLI/Utils/GrpcConnection.cs | 21 +++++++++++++++++++ 7 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 GrpcTodo.CLI/Utils/GrpcConnection.cs diff --git a/GrpcTodo.CLI/UseCases/AccountCreate/AccountCreateUseCase.cs b/GrpcTodo.CLI/UseCases/AccountCreate/AccountCreateUseCase.cs index cadc273..9affe7f 100644 --- a/GrpcTodo.CLI/UseCases/AccountCreate/AccountCreateUseCase.cs +++ b/GrpcTodo.CLI/UseCases/AccountCreate/AccountCreateUseCase.cs @@ -1,6 +1,5 @@ using GrpcTodo.CLI.Utils; using Grpc.Core; -using Grpc.Net.Client; using GrpcTodo.SharedKernel.Protos.User; using GrpcTodo.SharedKernel.Protos.User.Requests; using GrpcTodo.CLI.Lib; @@ -34,7 +33,7 @@ public override async Task ExecuteAsync() try { - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcConnection.CreateChannel(); var client = new User.UserClient(channel); diff --git a/GrpcTodo.CLI/UseCases/AccountLogin/AccountLoginUseCase.cs b/GrpcTodo.CLI/UseCases/AccountLogin/AccountLoginUseCase.cs index 03a8317..3bbf30f 100644 --- a/GrpcTodo.CLI/UseCases/AccountLogin/AccountLoginUseCase.cs +++ b/GrpcTodo.CLI/UseCases/AccountLogin/AccountLoginUseCase.cs @@ -1,6 +1,5 @@ using GrpcTodo.CLI.Utils; using Grpc.Core; -using Grpc.Net.Client; using GrpcTodo.SharedKernel.Protos.User; using GrpcTodo.SharedKernel.Protos.User.Requests; using GrpcTodo.CLI.Lib; @@ -34,7 +33,7 @@ public override async Task ExecuteAsync() try { - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcConnection.CreateChannel(); var client = new User.UserClient(channel); diff --git a/GrpcTodo.CLI/UseCases/AccountTokenUpdate/AccountTokenUpdateUseCase.cs b/GrpcTodo.CLI/UseCases/AccountTokenUpdate/AccountTokenUpdateUseCase.cs index 730742c..e985de9 100644 --- a/GrpcTodo.CLI/UseCases/AccountTokenUpdate/AccountTokenUpdateUseCase.cs +++ b/GrpcTodo.CLI/UseCases/AccountTokenUpdate/AccountTokenUpdateUseCase.cs @@ -1,5 +1,4 @@ using Grpc.Core; -using Grpc.Net.Client; using GrpcTodo.SharedKernel.Protos.User; using GrpcTodo.SharedKernel.Protos.User.Requests; using GrpcTodo.CLI.UseCases.Common; @@ -34,7 +33,7 @@ public override async Task ExecuteAsync() try { - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcConnection.CreateChannel(); var client = new User.UserClient(channel); diff --git a/GrpcTodo.CLI/UseCases/TaskCreate/TaskCreateUseCase.cs b/GrpcTodo.CLI/UseCases/TaskCreate/TaskCreateUseCase.cs index a3834b6..52044c7 100644 --- a/GrpcTodo.CLI/UseCases/TaskCreate/TaskCreateUseCase.cs +++ b/GrpcTodo.CLI/UseCases/TaskCreate/TaskCreateUseCase.cs @@ -1,5 +1,4 @@ using Grpc.Core; -using Grpc.Net.Client; using GrpcTodo.CLI.Lib; using GrpcTodo.CLI.UseCases.Common; using GrpcTodo.CLI.Utils; @@ -27,7 +26,7 @@ public override async Task ExecuteAsync() var prompt = createTaskPrompt.PromptName(); var name = prompt.Name; - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcConnection.CreateChannel(); var client = new TaskItem.TaskItemClient(channel); diff --git a/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs index 733fd0a..75a0bd4 100644 --- a/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs +++ b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs @@ -1,7 +1,4 @@ - -using Grpc.Net.Client; - -using GrpcTodo.CLI.Lib; +using GrpcTodo.CLI.Lib; using GrpcTodo.CLI.UseCases.Common; using GrpcTodo.CLI.Utils; using GrpcTodo.SharedKernel.Protos.Tasks; @@ -21,7 +18,7 @@ public override async Task ExecuteAsync() if (accessToken is null) throw new ArgumentNullException(nameof(accessToken), "you are not authenticated"); - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcConnection.CreateChannel(); var client = new TaskItem.TaskItemClient(channel); diff --git a/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs b/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs index 4f740be..078b6a9 100644 --- a/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs +++ b/GrpcTodo.CLI/UseCases/TaskList/TaskListUseCase.cs @@ -1,7 +1,5 @@ using System.Text; -using Grpc.Net.Client; - using GrpcTodo.CLI.Lib; using GrpcTodo.CLI.UseCases.Common; using GrpcTodo.CLI.Utils; @@ -26,7 +24,7 @@ public override async Task ExecuteAsync() if (accessToken is null) throw new ArgumentNullException(nameof(accessToken), "you are not authenticated"); - using var channel = GrpcChannel.ForAddress(Settings.ServerAddress); + using var channel = GrpcConnection.CreateChannel(); var client = new TaskItem.TaskItemClient(channel); diff --git a/GrpcTodo.CLI/Utils/GrpcConnection.cs b/GrpcTodo.CLI/Utils/GrpcConnection.cs new file mode 100644 index 0000000..ea6acc9 --- /dev/null +++ b/GrpcTodo.CLI/Utils/GrpcConnection.cs @@ -0,0 +1,21 @@ +using Grpc.Net.Client; + +namespace GrpcTodo.CLI.Utils; + +public static class GrpcConnection +{ + public static GrpcChannel CreateChannel() + { + var httpHandler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + }; + + var configurations = new GrpcChannelOptions + { + HttpHandler = httpHandler + }; + + return GrpcChannel.ForAddress(Settings.ServerAddress, configurations); + } +} \ No newline at end of file From 6f0f6ccb9ddbaf225485c0f64c8034561afbb18d Mon Sep 17 00:00:00 2001 From: Marcos Venicus Date: Tue, 6 Jun 2023 08:56:28 -0300 Subject: [PATCH 3/3] delete a task by your id --- .docker-compose.yml.swp | Bin 0 -> 12288 bytes .../UseCases/TaskDelete/TaskDeleteUseCase.cs | 7 ++++- .../Repositories/TaskRepository.cs | 17 +++++++++++ .../Domain/Repositories/ITaskRepository.cs | 2 ++ .../UseCases/Tasks/DeleteTaskUseCase.cs | 28 ++++++++++++++++++ .../GrpcServices/TaskGrpcService.cs | 14 +++++++++ GrpcTodo.Server/Infra/Queries/TaskQueries.cs | 2 ++ GrpcTodo.Server/IoC/DependencyInjection.cs | 1 + .../Exceptions/DeniedException.cs | 12 ++++++++ .../Protos/task/requests.proto | 5 ++++ .../Protos/task/responses.proto | 3 ++ .../Protos/task/service.proto | 1 + 12 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 .docker-compose.yml.swp create mode 100644 GrpcTodo.Server/Domain/UseCases/Tasks/DeleteTaskUseCase.cs create mode 100644 GrpcTodo.SharedKernel/Exceptions/DeniedException.cs diff --git a/.docker-compose.yml.swp b/.docker-compose.yml.swp new file mode 100644 index 0000000000000000000000000000000000000000..fb3bdea47604522f2ab403131db9e7088a868059 GIT binary patch literal 12288 zcmeI2KX21O7>BP&FrXF?NDQzX0aP(KPSREcZ&Zm818Njg7#PCEK1HUs&plsLRf2Sc z4MtY@05Gv4z5tzpnSp@`hBESVViOYD3dW-MqQ`dbeSLo(dx`RPD|c?*;`MTkp`BuE z``*BJXOFYbb;cs0J(XsGgWIOkYX@hHqJzxJ~*w?@uTc}klS@>e<0zY$lb>B=1kO4A42FL&z zAOmE843Ggb@c$Vw$qd`ZwvOamyON)0rq20SI*|b~KnBPF86X2>fDDiUGC&5%02v?y zhtPoV8GAd+*kBHe$N&Gu@Bi;78T$slfKT8xcm~h;6CVpCTM_b;5;}7j)9|K7wh>5K7bwY2D}6>z;lqT_Yt5*2FL&zAOmE8 z43GgbKnBPF88{pUI3HI&qv+W}sk7;6nL4bm!i^}qU>wcYL0@WzcXi1kG zupO3P6t{71?VULsbSfFs8ljEDMYt)pQ1*>^(JWr7)~=Q-_*9*GrCu2xN-T>dN}FP? zAQC-^oBb$=vD8o)j5iv@Q?U_QZzmSfI0r)%+2hG3H}=bi-51&pgRWiltcSKQjKCP2 zPr8~~^3vi$&B+dbF8QDIaNb&RZ{KJ)-A-fK;aRbICo5JTuHd&|>$>aSU1>KQo`^KX zWAUdmR^4XX;Tx&c!-$OgfmU%OW0ODaTxGFUr@CZ`FoD3mbryNnggcB~1lB``VYYtY R4PPj{Nok;BhtDs}v!4dq0UH1S literal 0 HcmV?d00001 diff --git a/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs index 75a0bd4..b9f8a71 100644 --- a/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs +++ b/GrpcTodo.CLI/UseCases/TaskDelete/TaskDeleteUseCase.cs @@ -35,6 +35,11 @@ public override async Task ExecuteAsync() var taskIdToDelete = taskDeletePrompt.PromptTask(tasks); - ConsoleWritter.Write($"the task [{taskIdToDelete}] will be deleted"); + await client.DeleteAsync(new TaskDeleteRequest { + TaskId = taskIdToDelete, + AccessToken = accessToken + }); + + ConsoleWritter.WriteSuccess($"task [{taskIdToDelete}] deleted successfully"); } } diff --git a/GrpcTodo.Server/Application/Repositories/TaskRepository.cs b/GrpcTodo.Server/Application/Repositories/TaskRepository.cs index b2f6bda..5e59751 100644 --- a/GrpcTodo.Server/Application/Repositories/TaskRepository.cs +++ b/GrpcTodo.Server/Application/Repositories/TaskRepository.cs @@ -34,6 +34,23 @@ public async Task CreateAsync(Guid id, string name, Guid userId) }); } + public async Task DeleteAsync(Guid id) + { + using var connection = _context.CreateConnection(); + + await connection.ExecuteAsync(TaskQueries.Delete, new + { + id + }); + } + + public async Task FindByShortIdAsync(string id) + { + using var connection = _context.CreateConnection(); + + return await connection.QueryFirstOrDefaultAsync(TaskQueries.FindByShortId, new { id = id + '%' }); + } + public async Task FindByNameAsync(string name) { using var connection = _context.CreateConnection(); diff --git a/GrpcTodo.Server/Domain/Repositories/ITaskRepository.cs b/GrpcTodo.Server/Domain/Repositories/ITaskRepository.cs index a8f39eb..07950ac 100644 --- a/GrpcTodo.Server/Domain/Repositories/ITaskRepository.cs +++ b/GrpcTodo.Server/Domain/Repositories/ITaskRepository.cs @@ -6,8 +6,10 @@ public interface ITaskRepository { public Task CreateAsync(Guid id, string name, Guid userId); public Task FindByNameAsync(string name); + public Task FindByShortIdAsync(string id); public Task UpdateTaskNameAsync(Guid id, string name); public Task CompleteAsync(Guid id); public Task UncompleteAsync(Guid id); public Task> ListAsync(Guid userId); + public Task DeleteAsync(Guid id); } diff --git a/GrpcTodo.Server/Domain/UseCases/Tasks/DeleteTaskUseCase.cs b/GrpcTodo.Server/Domain/UseCases/Tasks/DeleteTaskUseCase.cs new file mode 100644 index 0000000..f6cd003 --- /dev/null +++ b/GrpcTodo.Server/Domain/UseCases/Tasks/DeleteTaskUseCase.cs @@ -0,0 +1,28 @@ +using GrpcTodo.Server.Domain.Repositories; + +namespace GrpcTodo.Server.Domain.UseCases.Tasks; + +public sealed class DeleteTaskUseCase +{ + private readonly ITaskRepository _taskRepository; + + public DeleteTaskUseCase(ITaskRepository taskRepository) + { + _taskRepository = taskRepository; + } + + public async Task ExecuteAsync(Guid userId, DeleteTaskUseCaseInput request) + { + var task = await _taskRepository.FindByShortIdAsync(request.Id); + + if (task is null) + throw new NotFoundException("this task does not exists"); + + if (task.UserId != userId) + throw new DeniedException("this task does not belongs to you"); + + await _taskRepository.DeleteAsync(task.Id); + } +} + +public sealed record DeleteTaskUseCaseInput(string Id); \ No newline at end of file diff --git a/GrpcTodo.Server/GrpcServices/TaskGrpcService.cs b/GrpcTodo.Server/GrpcServices/TaskGrpcService.cs index a5147c5..ad5c747 100644 --- a/GrpcTodo.Server/GrpcServices/TaskGrpcService.cs +++ b/GrpcTodo.Server/GrpcServices/TaskGrpcService.cs @@ -14,15 +14,18 @@ public sealed class TaskGrpcService : TaskItem.TaskItemBase private readonly IAuthMiddleware _authMiddleware; private readonly CreateTaskUseCase _createTaskUseCase; private readonly ListAllTasksUseCase _listAllTasksUseCase; + private readonly DeleteTaskUseCase _deleteTaskUseCase; public TaskGrpcService( CreateTaskUseCase createTaskUseCase, ListAllTasksUseCase listAllTasksUseCase, + DeleteTaskUseCase deleteTaskUseCase, IAuthMiddleware authMiddleware) { _createTaskUseCase = createTaskUseCase; _listAllTasksUseCase = listAllTasksUseCase; _authMiddleware = authMiddleware; + _deleteTaskUseCase = deleteTaskUseCase; } public override async Task Create(TaskCreateRequest request, ServerCallContext context) @@ -60,4 +63,15 @@ public override async Task ListAll(TaskListRequest request, Se return tasks; } + + public override async Task Delete(TaskDeleteRequest request, ServerCallContext context) + { + var credentials = new Credentials(request.AccessToken); + + var input = new DeleteTaskUseCaseInput(request.TaskId); + + await _authMiddleware.Authenticate(credentials, input, _deleteTaskUseCase.ExecuteAsync); + + return new TaskDeleteResponse(); + } } diff --git a/GrpcTodo.Server/Infra/Queries/TaskQueries.cs b/GrpcTodo.Server/Infra/Queries/TaskQueries.cs index 59c8ac3..e1f5061 100644 --- a/GrpcTodo.Server/Infra/Queries/TaskQueries.cs +++ b/GrpcTodo.Server/Infra/Queries/TaskQueries.cs @@ -2,9 +2,11 @@ namespace GrpcTodo.Server.Infra.Queries; public static class TaskQueries { + public const string Delete = @"DELETE FROM ""task"" WHERE id = @id"; public const string Complete = ""; public const string Create = """INSERT INTO "task" (id, user_id, name, created_at) VALUES (@id, @userId, @name, @createdAt)"""; public const string FindByName = @"SELECT id, user_id as userId, name, completed, created_at as CreatedAt FROM ""task"" WHERE LOWER(name) = @name;"; + public const string FindByShortId = @"SELECT id, user_id as userId, name, completed, created_at as CreatedAt FROM ""task"" WHERE id::text LIKE @id;"; public const string List = """SELECT id, user_id as UserId, name, completed, created_at as CreatedAt FROM "task" WHERE user_id = @userId;"""; public const string Uncomplete = ""; public const string UpdateTaskName = ""; diff --git a/GrpcTodo.Server/IoC/DependencyInjection.cs b/GrpcTodo.Server/IoC/DependencyInjection.cs index e4ccb4f..1bf2544 100644 --- a/GrpcTodo.Server/IoC/DependencyInjection.cs +++ b/GrpcTodo.Server/IoC/DependencyInjection.cs @@ -27,6 +27,7 @@ public static IServiceCollection AddIoC(this IServiceCollection services) services.AddSingleton(); // use cases + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/GrpcTodo.SharedKernel/Exceptions/DeniedException.cs b/GrpcTodo.SharedKernel/Exceptions/DeniedException.cs new file mode 100644 index 0000000..0791ef1 --- /dev/null +++ b/GrpcTodo.SharedKernel/Exceptions/DeniedException.cs @@ -0,0 +1,12 @@ +using Grpc.Core; + +namespace GrpcTodo.SharedKernel.Exceptions; + +public sealed class DeniedException : RpcException +{ + public DeniedException(string message) : base(new Status( + StatusCode.PermissionDenied, + message + ), message) + { } +} \ No newline at end of file diff --git a/GrpcTodo.SharedKernel/Protos/task/requests.proto b/GrpcTodo.SharedKernel/Protos/task/requests.proto index dd29d8b..4628aad 100644 --- a/GrpcTodo.SharedKernel/Protos/task/requests.proto +++ b/GrpcTodo.SharedKernel/Protos/task/requests.proto @@ -10,3 +10,8 @@ message TaskCreateRequest { message TaskListRequest { string accessToken = 1; } + +message TaskDeleteRequest { + string accessToken = 1; + string taskId = 2; +} \ No newline at end of file diff --git a/GrpcTodo.SharedKernel/Protos/task/responses.proto b/GrpcTodo.SharedKernel/Protos/task/responses.proto index a3b3560..56eb9e1 100644 --- a/GrpcTodo.SharedKernel/Protos/task/responses.proto +++ b/GrpcTodo.SharedKernel/Protos/task/responses.proto @@ -6,6 +6,9 @@ message TaskCreateResponse { string id = 1; } +message TaskDeleteResponse { +} + message TaskListResponseItem { string id = 1; string name = 2; diff --git a/GrpcTodo.SharedKernel/Protos/task/service.proto b/GrpcTodo.SharedKernel/Protos/task/service.proto index 405214a..4d2ef99 100644 --- a/GrpcTodo.SharedKernel/Protos/task/service.proto +++ b/GrpcTodo.SharedKernel/Protos/task/service.proto @@ -8,5 +8,6 @@ package grpc_todo.shared_kernel.protos.tasks; service TaskItem { rpc Create(requests.TaskCreateRequest) returns (responses.TaskCreateResponse); + rpc Delete(requests.TaskDeleteRequest) returns (responses.TaskDeleteResponse); rpc ListAll(requests.TaskListRequest) returns (responses.TaskListResponse); }