Skip to content

Commit

Permalink
Merge pull request #6 from romandykyi/basic-todo-list-management
Browse files Browse the repository at this point in the history
Basic To-do List Management
  • Loading branch information
romandykyi authored Feb 14, 2024
2 parents c45f78a + 796a13b commit b0d010d
Show file tree
Hide file tree
Showing 37 changed files with 2,311 additions and 453 deletions.
12 changes: 8 additions & 4 deletions AdvancedTodoList.Core/Dtos/TodoListDtos.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
namespace AdvancedTodoList.Core.Dtos;

public record TodoListItemView(int Id);
/// <summary>
/// DTO for creating/editing a to-do list.
/// </summary>
public record TodoListCreateDto(string Name, string Description);

public record TodoListCreateDto(string Name);

public record TodoListGetByIdDto(string Id, string Name, TodoListItemView[] TodoItems);
/// <summary>
/// DTO for a full view of a to-do list.
/// </summary>
public record TodoListGetByIdDto(string Id, string Name, string Description);
30 changes: 30 additions & 0 deletions AdvancedTodoList.Core/Dtos/TodoListItemDtos.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using AdvancedTodoList.Core.Models.TodoLists;

namespace AdvancedTodoList.Core.Dtos;

/// <summary>
/// DTO for creating/editing a to-do list item.
/// </summary>
public record TodoItemCreateDto(string Name, string Description, DateTime? DeadlineDate);

/// <summary>
/// DTO for changing the state of a to-do list item.
/// </summary>
public record TodoItemUpdateStateDto(TodoItemState State);

/// <summary>
/// DTO for a full view of a to-do list item.
/// </summary>
public record TodoItemGetByIdDto(
int Id, string TodoListId, string Name,
string Description, DateTime? DeadlineDate,
TodoItemState State
);

/// <summary>
/// DTO for a partial view of a to-do list item.
/// </summary>
public record TodoItemPreviewDto(
int Id, string TodoListId, string Name,
DateTime? DeadlineDate, TodoItemState State
);
19 changes: 19 additions & 0 deletions AdvancedTodoList.Core/Mapping/MappingGlobalSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Mapster;

namespace AdvancedTodoList.Core.Mapping;

/// <summary>
/// Class that defines global mapping settings.
/// </summary>
public static class MappingGlobalSettings
{
/// <summary>
/// Apply global mapping settings.
/// </summary>
public static void Apply()
{
// Convert null strings into empty strings and trim strings
TypeAdapterConfig.GlobalSettings.Default
.AddDestinationTransform((string? dest) => dest != null ? dest.Trim() : string.Empty);
}
}
2 changes: 1 addition & 1 deletion AdvancedTodoList.Core/Models/ApplicationUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace AdvancedTodoList.Core.Models;

public class ApplicationUser : IdentityUser
public class ApplicationUser : IdentityUser, IEntity<string>
{
}
10 changes: 10 additions & 0 deletions AdvancedTodoList.Core/Models/IEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace AdvancedTodoList.Core.Models;

/// <summary>
/// An interface that represents an entity with an ID property.
/// </summary>
/// <typeparam name="TId">Type of the entity ID.</typeparam>
public interface IEntity<TId> where TId : IEquatable<TId>
{
TId Id { get; }
}
56 changes: 28 additions & 28 deletions AdvancedTodoList.Core/Models/TodoLists/TodoItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@ namespace AdvancedTodoList.Core.Models.TodoLists;
/// <summary>
/// Represents a to-do list item entity.
/// </summary>
public class TodoItem
public class TodoItem : IEntity<int>
{
/// <summary>
/// An unique identifier for the to-do list item.
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// Name (title) of the to-do item.
/// </summary>
[MaxLength(NameMaxLength)]
public string Name { get; set; } = null!;
/// <summary>
/// Description of the to-do item.
/// </summary>
[MaxLength(DescriptionMaxLength)]
public string Description { get; set; } = null!;
/// <summary>
/// Current state of the to-do item.
/// </summary>
public TodoItemState State { get; set; }
/// <summary>
/// An unique identifier for the to-do list item.
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// Name (title) of the to-do item.
/// </summary>
[MaxLength(NameMaxLength)]
public string Name { get; set; } = null!;
/// <summary>
/// Description of the to-do item.
/// </summary>
[MaxLength(DescriptionMaxLength)]
public string Description { get; set; } = null!;
/// <summary>
/// Current state of the to-do item.
/// </summary>
public TodoItemState State { get; set; }
/// <summary>
/// Deadline date for the todo item. Can be null.
/// </summary>
Expand All @@ -42,15 +42,15 @@ public class TodoItem
/// Maximum allowed length of <see cref="Name"/>.
/// </summary>
public const int NameMaxLength = 100;
/// <summary>
/// Maximum allowed length of <see cref="Description"/>.
/// </summary>
public const int DescriptionMaxLength = 10_000;
/// <summary>
/// Maximum allowed length of <see cref="Description"/>.
/// </summary>
public const int DescriptionMaxLength = 10_000;

/// <summary>
/// To-do list associated with this to-do item.
/// </summary>
public TodoList TodoList { get; set; } = null!;
/// <summary>
/// To-do list associated with this to-do item.
/// </summary>
public TodoList TodoList { get; set; } = null!;
}

/// <summary>
Expand Down
24 changes: 12 additions & 12 deletions AdvancedTodoList.Core/Models/TodoLists/TodoList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ namespace AdvancedTodoList.Core.Models.TodoLists;
/// <summary>
/// Represents a to-do list entity.
/// </summary>
public class TodoList
public class TodoList : IEntity<string>
{
/// <summary>
/// An unique identifier for the to-do list.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; } = null!;
/// <summary>
/// An unique identifier for the to-do list.
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; } = null!;

/// <summary>
/// Name (title) of the to-do list.
/// </summary>
[MaxLength(NameMaxLength)]
public string Name { get; set; } = null!;
/// <summary>
/// Name (title) of the to-do list.
/// </summary>
[MaxLength(NameMaxLength)]
public string Name { get; set; } = null!;
/// <summary>
/// Description of the to-do list.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions AdvancedTodoList.Core/Services/IEntityExistenceChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using AdvancedTodoList.Core.Models;

namespace AdvancedTodoList.Core.Services;

/// <summary>
/// An interface for the service that checks whether an entity with an ID exists.
/// </summary>
public interface IEntityExistenceChecker
{
/// <summary>
/// Asynchronously checks whether an entity of type <typeparamref name="TEntity"/> with an ID
/// <paramref name="id"/> of type <typeparamref name="TId"/> exists.
/// </summary>
/// <typeparam name="TEntity">Type of the entity.</typeparam>
/// <typeparam name="TId">Type which ID of the entity has.</typeparam>
/// <param name="id">ID of the entity which existence is checked.</param>
/// <returns>
/// A task representing the asynchronous operation. The task result contains
/// <see langword="true"/> if entity with the given ID exists; otherwise
/// <see langword="false"/>.
/// </returns>
Task<bool> ExistsAsync<TEntity, TId>(TId id)
where TEntity : class, IEntity<TId>
where TId : IEquatable<TId>;
}
91 changes: 91 additions & 0 deletions AdvancedTodoList.Core/Services/ITodoItemsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using AdvancedTodoList.Core.Dtos;
using AdvancedTodoList.Core.Models.TodoLists;

namespace AdvancedTodoList.Core.Services;

/// <summary>
/// Interface for a service that manages to-do list items.
/// </summary>
public interface ITodoItemsService
{
/// <summary>
/// Retrieves to-do list items of the list with the specified ID.
/// </summary>
/// <remarks>
/// Does not throw exceptions if ID is invalid.
/// </remarks>
/// <param name="id">The ID of the to-do list which items will be retrieved.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains a collection of <see cref="TodoItemPreviewDto"/> objects.
/// </returns>
public Task<IEnumerable<TodoItemPreviewDto>> GetItemsOfListAsync(string id);

/// <summary>
/// Retrieves a to-do list ID of the to-do list item.
/// </summary>
/// <param name="id">ID of the to-do list item.</param>
/// <returns>
/// A task representing the asynchronous operation. The task result contains
/// an ID of the to-do list which owns a to-do list item with the specified ID if it's found;
/// otherwise, returns <see langword="null"/>.
/// </returns>
public Task<string?> GetTodoListByIdAsync(int id);

/// <summary>
/// Retrieves a to-do list item by its ID asynchronously.
/// </summary>
/// <param name="id">The ID of the to-do list item to retrieve.</param>
/// <returns>
/// A task representing the asynchronous operation. The task result contains
/// a <see cref="TodoItemGetByIdDto"/> object if the specified ID is found;
/// otherwise, returns <see langword="null"/>.
/// </returns>
public Task<TodoItemGetByIdDto?> GetByIdAsync(int id);

/// <summary>
/// Creates a new to-do list item asynchronously.
/// </summary>
/// <param name="todoListId">The ID of the to-do list to associate the item with.</param>
/// <param name="dto">The DTO containing information for creating the to-do list item.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains the created <see cref="TodoItem"/>.
/// </returns>
public Task<TodoItem> CreateAsync(string todoListId, TodoItemCreateDto dto);

/// <summary>
/// Edits a to-do list item asynchronously.
/// </summary>
/// <param name="id">The ID of the to-do list item to edit.</param>
/// <param name="dto">The DTO containing information for editing the to-do list item.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// </returns>
public Task<bool> EditAsync(int id, TodoItemCreateDto dto);

/// <summary>
/// Updates the state of a to-do list item asynchronously.
/// </summary>
/// <param name="id">The ID of the to-do list item to update the state.</param>
/// <param name="dto">The DTO containing information for updating the state of the to-do list item.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// </returns>
public Task<bool> UpdateStateAsync(int id, TodoItemUpdateStateDto dto);

/// <summary>
/// Deletes a to-do list item asynchronously.
/// </summary>
/// <param name="id">The ID of the to-do list item to delete.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if the entity was not found.
/// </returns>
public Task<bool> DeleteAsync(int id);
}
32 changes: 29 additions & 3 deletions AdvancedTodoList.Core/Services/ITodoListsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

namespace AdvancedTodoList.Core.Services;

/// <summary>
/// Interface for a service that manages to-do lists.
/// </summary>
public interface ITodoListsService
{
/// <summary>
/// Retrieves a to-do list by its unique identifier asynchronously.
/// Retrieves a to-do list by its ID asynchronously.
/// </summary>
/// <param name="id">The unique identifier of the to-do list to retrieve.</param>
/// <param name="id">The ID of the to-do list to retrieve.</param>
/// <returns>
/// A task representing the asynchronous operation. The task result contains
/// a <see cref="TodoListGetByIdDto"/> object if the specified ID is found;
Expand All @@ -19,10 +22,33 @@ public interface ITodoListsService
/// <summary>
/// Creates a new to-do list asynchronously.
/// </summary>
/// <param name="dto">The data transfer object containing information for creating the todo list.</param>
/// <param name="dto">The DTO containing information for creating the to-do list.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains the created <see cref="TodoList"/>.
/// </returns>
public Task<TodoList> CreateAsync(TodoListCreateDto dto);

/// <summary>
/// Edits a to-do list asynchronously.
/// </summary>
/// <param name="id">The ID of the to-do list to edit.</param>
/// <param name="dto">The DTO containing information for editing the to-do list.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if entity was not found.
/// </returns>
public Task<bool> EditAsync(string id, TodoListCreateDto dto);

/// <summary>
/// Deletes a to-do list asynchronously.
/// </summary>
/// <param name="id">The ID of the to-do list to edit.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// The task result contains <see langword="true"/> on success;
/// otherwise <see langword="false"/> if entity was not found.
/// </returns>
public Task<bool> DeleteAsync(string id);
}
28 changes: 28 additions & 0 deletions AdvancedTodoList.Core/Validation/TodoItemCreateDtoValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using AdvancedTodoList.Core.Dtos;
using FluentValidation;

namespace AdvancedTodoList.Core.Validation;

/// <summary>
/// Validator class for <see cref="TodoItemCreateDto" />
/// </summary>
public class TodoItemCreateDtoValidator : AbstractValidator<TodoItemCreateDto>
{
public TodoItemCreateDtoValidator()
{
// Name is required
RuleFor(x => x.Name)
.NotEmpty()
.WithErrorCode(ValidationErrorCodes.PropertyRequired);

// Description is not null
RuleFor(x => x.Description)
.NotNull()
.WithErrorCode(ValidationErrorCodes.PropertyRequired);

// Deadline date should be after the current date
RuleFor(x => x.DeadlineDate)
.GreaterThan(DateTime.UtcNow)
.WithErrorCode(ValidationErrorCodes.PropertyOutOfRange);
}
}
Loading

0 comments on commit b0d010d

Please sign in to comment.