Add redux
This commit is contained in:
@@ -7,5 +7,5 @@ namespace Todo.Api.Controllers;
|
||||
[ApiController]
|
||||
public class ApiController : ControllerBase
|
||||
{
|
||||
public IMediator Mediator => HttpContext.RequestServices.GetRequiredService<IMediator>();
|
||||
protected IMediator Mediator => HttpContext.RequestServices.GetRequiredService<IMediator>();
|
||||
}
|
@@ -47,8 +47,33 @@ public class TodosController : ApiController
|
||||
public async Task<ActionResult<IEnumerable<Core.Entities.Todo>>> GetTodos()
|
||||
=> Ok(await _todoRepository.GetTodosAsync());
|
||||
|
||||
[HttpGet("not-done")]
|
||||
public async Task<ActionResult<IEnumerable<Core.Entities.Todo>>>
|
||||
GetNotDoneTodos()
|
||||
=> Ok(await _todoRepository.GetNotDoneTodos());
|
||||
[HttpPut("{todoId}")]
|
||||
public async Task<ActionResult> ReplaceTodo([FromRoute] string todoId, [FromBody] ReplaceTodoRequest request)
|
||||
{
|
||||
await Mediator.Send(request.To(todoId));
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
public record ReplaceTodoRequest
|
||||
{
|
||||
[Required]
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; init; }
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; init; }
|
||||
|
||||
[JsonPropertyName("project")]
|
||||
public string? Project { get; init; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public bool Status { get; init; }
|
||||
|
||||
internal ReplaceTodoCommand To(string id) => new(
|
||||
id,
|
||||
Title,
|
||||
Project,
|
||||
Description,
|
||||
Status);
|
||||
}
|
||||
}
|
@@ -27,8 +27,9 @@ public class TodoPublisher : ITodoPublisher
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Publish(
|
||||
string todoId,
|
||||
public async Task Publish<T>(
|
||||
string eventType,
|
||||
T message,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var userId = _currentUserService.GetUserId() ??
|
||||
@@ -36,14 +37,33 @@ public class TodoPublisher : ITodoPublisher
|
||||
var connections =
|
||||
await _userConnectionStore.GetConnectionsAsync(userId);
|
||||
|
||||
await _hubContext
|
||||
.Clients
|
||||
.Clients(connections)
|
||||
.SendAsync(
|
||||
"todoCreated",
|
||||
todoId,
|
||||
cancellationToken);
|
||||
switch (eventType)
|
||||
{
|
||||
case "todoCreated":
|
||||
await _hubContext
|
||||
.Clients
|
||||
.Clients(connections)
|
||||
.SendAsync(
|
||||
"todoCreated",
|
||||
message,
|
||||
cancellationToken);
|
||||
break;
|
||||
|
||||
_logger.LogInformation("todo created {TodoId}", todoId);
|
||||
case "todoUpdated":
|
||||
await _hubContext
|
||||
.Clients
|
||||
.Clients(connections)
|
||||
.SendAsync(
|
||||
"todoUpdated",
|
||||
message,
|
||||
cancellationToken);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
_logger.LogInformation(
|
||||
"todo event: {EventType} with message: {Message}",
|
||||
eventType,
|
||||
message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
using System.Threading;
|
||||
using MediatR;
|
||||
using Todo.Core.Application.Notifications.Todo;
|
||||
using Todo.Core.Interfaces.Persistence;
|
||||
using Todo.Core.Interfaces.User;
|
||||
|
||||
namespace Todo.Core.Application.Commands.Todo;
|
||||
|
||||
public record ReplaceTodoCommand(
|
||||
string Id,
|
||||
string Title,
|
||||
string Project,
|
||||
string Description,
|
||||
bool Status) : IRequest<Unit>
|
||||
{
|
||||
internal class Handler : IRequestHandler<ReplaceTodoCommand, Unit>
|
||||
{
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
private readonly ITodoRepository _todoRepository;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public Handler(
|
||||
ICurrentUserService currentUserService,
|
||||
ITodoRepository todoRepository,
|
||||
IMediator mediator)
|
||||
{
|
||||
_currentUserService = currentUserService;
|
||||
_todoRepository = todoRepository;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(ReplaceTodoCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var userId = _currentUserService.GetUserId();
|
||||
if (userId is null)
|
||||
throw new InvalidOperationException("User was not found");
|
||||
//TODO: Make sure the author actually owns the todo
|
||||
await _todoRepository.UpdateTodoAsync(request.To(userId));
|
||||
|
||||
await _mediator.Publish(new TodoUpdated(request.Id), cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private Entities.Todo To(string authorId) => new()
|
||||
{
|
||||
Id = Id,
|
||||
Description = Description,
|
||||
Project = Project,
|
||||
Status = Status,
|
||||
Title = Title,
|
||||
AuthorId = authorId
|
||||
};
|
||||
}
|
@@ -20,6 +20,7 @@ public record TodoCreated(
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await _todoPublisher.Publish(
|
||||
"todoCreated",
|
||||
JsonSerializer.Serialize(notification),
|
||||
cancellationToken);
|
||||
}
|
||||
|
@@ -0,0 +1,28 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using MediatR;
|
||||
using Todo.Core.Interfaces.Publisher;
|
||||
|
||||
namespace Todo.Core.Application.Notifications.Todo;
|
||||
|
||||
public record TodoUpdated(
|
||||
[property: JsonPropertyName("todoId")] string TodoId) : INotification
|
||||
{
|
||||
internal class Handler : INotificationHandler<TodoUpdated>
|
||||
{
|
||||
private readonly ITodoPublisher _todoPublisher;
|
||||
|
||||
public Handler(ITodoPublisher todoPublisher) => _todoPublisher = todoPublisher;
|
||||
|
||||
public async Task Handle(
|
||||
TodoUpdated notification,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await _todoPublisher.Publish(
|
||||
"todoUpdated",
|
||||
JsonSerializer.Serialize(notification),
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,5 +4,5 @@ namespace Todo.Core.Interfaces.Publisher;
|
||||
|
||||
public interface ITodoPublisher
|
||||
{
|
||||
Task Publish(string todoId, CancellationToken cancellationToken = new());
|
||||
Task Publish<T>(string eventType, T message, CancellationToken cancellationToken = new());
|
||||
}
|
@@ -16,4 +16,4 @@ public record TodoViewModel(
|
||||
t.AuthorId,
|
||||
t.Project,
|
||||
t.Description);
|
||||
};
|
||||
}
|
@@ -39,7 +39,14 @@ internal class InMemoryUserConnectionStore : IUserConnectionStore
|
||||
ConnectedUsers.TryGetValue(userId, out var existingUserConnectionIds);
|
||||
|
||||
// remove the connection id from the List
|
||||
existingUserConnectionIds?.Remove(connectionId);
|
||||
try
|
||||
{
|
||||
existingUserConnectionIds?.Remove(connectionId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
// If there are no connection ids in the List, delete the user from the global cache (ConnectedUsers).
|
||||
if (existingUserConnectionIds?.Count == 0)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using MongoDB.Driver;
|
||||
using Todo.Core.Interfaces.Persistence;
|
||||
using Todo.Persistence.Mongo.Repositories.Dtos;
|
||||
@@ -35,7 +36,8 @@ public class TodoRepository : ITodoRepository
|
||||
Title = todo.Title,
|
||||
Status = false,
|
||||
Project = todo.ProjectName,
|
||||
Description = todo.Description
|
||||
Description = todo.Description,
|
||||
AuthorId = todo.AuthorId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,7 +54,8 @@ public class TodoRepository : ITodoRepository
|
||||
Title = t.Title,
|
||||
Status = t.Status,
|
||||
Project = t.ProjectName,
|
||||
Description = t.Description
|
||||
Description = t.Description,
|
||||
AuthorId = t.AuthorId
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,7 +87,8 @@ public class TodoRepository : ITodoRepository
|
||||
Project = todo.ProjectName,
|
||||
Status = todo.Status,
|
||||
Title = todo.Title,
|
||||
Description = todo.Description
|
||||
Description = todo.Description,
|
||||
AuthorId = todo.AuthorId
|
||||
};
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user