From 575e4f5c8b1d9d1ef07abbc01e8c946ab2bdd50d Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 15:02:03 +0800 Subject: [PATCH 1/8] add comments table and endpoints --- .../Controllers/CommentController.cs | 51 ++++++++ taskmaster-api/Controllers/TaskController.cs | 7 ++ .../Data/Contexts/ApplicationDbContext.cs | 4 +- taskmaster-api/Data/DTOs/ActivityLogDto.cs | 6 + taskmaster-api/Data/DTOs/AttachmentDto.cs | 6 + taskmaster-api/Data/DTOs/CommentDto.cs | 34 ++++++ taskmaster-api/Data/DTOs/NotificationDto.cs | 6 + taskmaster-api/Data/DTOs/SettingDto.cs | 6 + taskmaster-api/Data/DTOs/TagDto.cs | 6 + taskmaster-api/Data/DTOs/TaskDto.cs | 2 +- .../Data/Entities/ActivityLogEntity.cs | 6 + .../Data/Entities/AttachmentEntity.cs | 6 + taskmaster-api/Data/Entities/CommentEntity.cs | 36 ++++++ .../Data/Entities/NotificationEntity.cs | 6 + taskmaster-api/Data/Entities/SettingEntity.cs | 6 + taskmaster-api/Data/Entities/TagEntity.cs | 6 + taskmaster-api/Data/Entities/TaskEntity.cs | 6 +- .../Data/Repositories/CommentRepository.cs | 58 ++++++++++ .../Interface/ICommentRepository.cs | 13 +++ ... 20231211062434_InitialCreate.Designer.cs} | 55 ++++++++- ...ate.cs => 20231211062434_InitialCreate.cs} | 44 ++++++- .../ApplicationDbContextModelSnapshot.cs | 53 +++++++++ taskmaster-api/Program.cs | 2 + taskmaster-api/Services/CommentService.cs | 109 ++++++++++++++++++ .../Services/Interface/ICommentService.cs | 14 +++ taskmaster-api/Utilities/DtoHelpers.cs | 26 ----- taskmaster-api/Utilities/EntityHelpers.cs | 21 ++++ 27 files changed, 560 insertions(+), 35 deletions(-) create mode 100644 taskmaster-api/Controllers/CommentController.cs create mode 100644 taskmaster-api/Data/DTOs/ActivityLogDto.cs create mode 100644 taskmaster-api/Data/DTOs/AttachmentDto.cs create mode 100644 taskmaster-api/Data/DTOs/CommentDto.cs create mode 100644 taskmaster-api/Data/DTOs/NotificationDto.cs create mode 100644 taskmaster-api/Data/DTOs/SettingDto.cs create mode 100644 taskmaster-api/Data/DTOs/TagDto.cs create mode 100644 taskmaster-api/Data/Entities/ActivityLogEntity.cs create mode 100644 taskmaster-api/Data/Entities/AttachmentEntity.cs create mode 100644 taskmaster-api/Data/Entities/CommentEntity.cs create mode 100644 taskmaster-api/Data/Entities/NotificationEntity.cs create mode 100644 taskmaster-api/Data/Entities/SettingEntity.cs create mode 100644 taskmaster-api/Data/Entities/TagEntity.cs create mode 100644 taskmaster-api/Data/Repositories/CommentRepository.cs create mode 100644 taskmaster-api/Data/Repositories/Interface/ICommentRepository.cs rename taskmaster-api/Migrations/{20231211053644_InitialCreate.Designer.cs => 20231211062434_InitialCreate.Designer.cs} (85%) rename taskmaster-api/Migrations/{20231211053644_InitialCreate.cs => 20231211062434_InitialCreate.cs} (85%) create mode 100644 taskmaster-api/Services/CommentService.cs create mode 100644 taskmaster-api/Services/Interface/ICommentService.cs delete mode 100644 taskmaster-api/Utilities/DtoHelpers.cs diff --git a/taskmaster-api/Controllers/CommentController.cs b/taskmaster-api/Controllers/CommentController.cs new file mode 100644 index 0000000..45dc1a2 --- /dev/null +++ b/taskmaster-api/Controllers/CommentController.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Services; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class CommentController : ApplicationControllerBase + { + private readonly ICommentService _commentService; + + public CommentController(ICommentService commentService) + { + _commentService = commentService; + } + + [HttpGet] + public IActionResult GetAllComments() + { + return ToHttpResult>(_commentService.GetAllComments()); + } + + [HttpGet("{id}")] + public IActionResult GetComment(int id) + { + return ToHttpResult(_commentService.GetCommentById(id)); + } + + [HttpPost] + public IActionResult CreateComment(CommentDto commentDto) + { + return ToHttpResult(_commentService.CreateComment(commentDto)); + } + + [HttpPut("{id}")] + public IActionResult UpdateComment(int id, CommentDto commentDto) + { + return ToHttpResult(_commentService.UpdateComment(id, commentDto)); + } + + [HttpDelete("{id}")] + public IActionResult DeleteComment(int id) + { + return ToHttpResult(_commentService.DeleteComment(id)); + } + } +} diff --git a/taskmaster-api/Controllers/TaskController.cs b/taskmaster-api/Controllers/TaskController.cs index cae606c..0aca530 100644 --- a/taskmaster-api/Controllers/TaskController.cs +++ b/taskmaster-api/Controllers/TaskController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using taskmaster_api.Data.DTOs; using taskmaster_api.Data.Models; +using taskmaster_api.Services; using taskmaster_api.Services.Interface; namespace taskmaster_api.Controllers @@ -18,6 +19,12 @@ public TaskController(ITaskService taskService) _taskService = taskService; } + [HttpGet] + public IActionResult GetAllTasks() + { + return ToHttpResult>(_taskService.GetAllTasks()); + } + [HttpGet("{id}")] public IActionResult GetTask(int id) { diff --git a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs index c13ac0a..cd58268 100644 --- a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs +++ b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using taskmaster_api.Data.Entities; @@ -11,5 +12,6 @@ public ApplicationDbContext(DbContextOptions options) : base(options) } public virtual DbSet Tasks { get; set; } + public virtual DbSet Comments { get; set; } } } diff --git a/taskmaster-api/Data/DTOs/ActivityLogDto.cs b/taskmaster-api/Data/DTOs/ActivityLogDto.cs new file mode 100644 index 0000000..de3696a --- /dev/null +++ b/taskmaster-api/Data/DTOs/ActivityLogDto.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.DTOs +{ + public class ActivityLogDto + { + } +} diff --git a/taskmaster-api/Data/DTOs/AttachmentDto.cs b/taskmaster-api/Data/DTOs/AttachmentDto.cs new file mode 100644 index 0000000..4c27fb9 --- /dev/null +++ b/taskmaster-api/Data/DTOs/AttachmentDto.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.DTOs +{ + public class AttachmentDto + { + } +} diff --git a/taskmaster-api/Data/DTOs/CommentDto.cs b/taskmaster-api/Data/DTOs/CommentDto.cs new file mode 100644 index 0000000..5df7cc6 --- /dev/null +++ b/taskmaster-api/Data/DTOs/CommentDto.cs @@ -0,0 +1,34 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.Entities; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.DTOs +{ + public class CommentDto : IDto + { + public int? Id { get; set; } + public string UserId { get; set; } + public int TaskId { get; set; } + public string Content { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + + public CommentDto() + { + if (Id.HasValue) + { + UpdatedAt = DateTime.UtcNow; + } + else + { + CreatedAt = DateTime.UtcNow; + UpdatedAt = DateTime.UtcNow; + } + } + + public CommentEntity ToEntity() + { + return EntityHelpers.ToEntity(this); + } + } +} diff --git a/taskmaster-api/Data/DTOs/NotificationDto.cs b/taskmaster-api/Data/DTOs/NotificationDto.cs new file mode 100644 index 0000000..c03f5b2 --- /dev/null +++ b/taskmaster-api/Data/DTOs/NotificationDto.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.DTOs +{ + public class NotificationDto + { + } +} diff --git a/taskmaster-api/Data/DTOs/SettingDto.cs b/taskmaster-api/Data/DTOs/SettingDto.cs new file mode 100644 index 0000000..7557772 --- /dev/null +++ b/taskmaster-api/Data/DTOs/SettingDto.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.DTOs +{ + public class SettingDto + { + } +} diff --git a/taskmaster-api/Data/DTOs/TagDto.cs b/taskmaster-api/Data/DTOs/TagDto.cs new file mode 100644 index 0000000..7115e14 --- /dev/null +++ b/taskmaster-api/Data/DTOs/TagDto.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.DTOs +{ + public class TagDto + { + } +} diff --git a/taskmaster-api/Data/DTOs/TaskDto.cs b/taskmaster-api/Data/DTOs/TaskDto.cs index 2f42828..ee78f66 100644 --- a/taskmaster-api/Data/DTOs/TaskDto.cs +++ b/taskmaster-api/Data/DTOs/TaskDto.cs @@ -30,7 +30,7 @@ public TaskDto() public TaskEntity ToEntity() { - return DtoHelpers.ToEntity(this); + return EntityHelpers.ToEntity(this); } } } diff --git a/taskmaster-api/Data/Entities/ActivityLogEntity.cs b/taskmaster-api/Data/Entities/ActivityLogEntity.cs new file mode 100644 index 0000000..226fa57 --- /dev/null +++ b/taskmaster-api/Data/Entities/ActivityLogEntity.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.Entities +{ + public class ActivityLogEntity + { + } +} diff --git a/taskmaster-api/Data/Entities/AttachmentEntity.cs b/taskmaster-api/Data/Entities/AttachmentEntity.cs new file mode 100644 index 0000000..c75bc61 --- /dev/null +++ b/taskmaster-api/Data/Entities/AttachmentEntity.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.Entities +{ + public class AttachmentEntity + { + } +} diff --git a/taskmaster-api/Data/Entities/CommentEntity.cs b/taskmaster-api/Data/Entities/CommentEntity.cs new file mode 100644 index 0000000..94d7cfd --- /dev/null +++ b/taskmaster-api/Data/Entities/CommentEntity.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Entities.Interface; +using taskmaster_api.Utilities; +using Microsoft.AspNetCore.Identity; + +namespace taskmaster_api.Data.Entities +{ + public class CommentEntity : IEntity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int? Id { get; set; } + + [Required] + public string UserId { get; set; } + public IdentityUser User { get; set; } + + [Required] + public int TaskId { get; set; } + public TaskEntity Task { get; set; } + + [Required] + public string Content { get; set; } + + public DateTime CreatedAt { get; set; } + + public DateTime UpdatedAt { get; set; } + + public CommentDto ToDto() + { + return EntityHelpers.ToDto(this); + } + } +} diff --git a/taskmaster-api/Data/Entities/NotificationEntity.cs b/taskmaster-api/Data/Entities/NotificationEntity.cs new file mode 100644 index 0000000..5e1aa80 --- /dev/null +++ b/taskmaster-api/Data/Entities/NotificationEntity.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.Entities +{ + public class NotificationEntity + { + } +} diff --git a/taskmaster-api/Data/Entities/SettingEntity.cs b/taskmaster-api/Data/Entities/SettingEntity.cs new file mode 100644 index 0000000..099af6b --- /dev/null +++ b/taskmaster-api/Data/Entities/SettingEntity.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.Entities +{ + public class SettingEntity + { + } +} diff --git a/taskmaster-api/Data/Entities/TagEntity.cs b/taskmaster-api/Data/Entities/TagEntity.cs new file mode 100644 index 0000000..269a422 --- /dev/null +++ b/taskmaster-api/Data/Entities/TagEntity.cs @@ -0,0 +1,6 @@ +namespace taskmaster_api.Data.Entities +{ + public class TagEntity + { + } +} diff --git a/taskmaster-api/Data/Entities/TaskEntity.cs b/taskmaster-api/Data/Entities/TaskEntity.cs index 8a54c76..1642b2c 100644 --- a/taskmaster-api/Data/Entities/TaskEntity.cs +++ b/taskmaster-api/Data/Entities/TaskEntity.cs @@ -1,13 +1,9 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Microsoft.EntityFrameworkCore; -using System.ComponentModel; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using taskmaster_api.Data.DTOs; using taskmaster_api.Data.Entities.Interface; using taskmaster_api.Utilities; -using System.Runtime.Serialization; namespace taskmaster_api.Data.Entities { diff --git a/taskmaster-api/Data/Repositories/CommentRepository.cs b/taskmaster-api/Data/Repositories/CommentRepository.cs new file mode 100644 index 0000000..7760a1e --- /dev/null +++ b/taskmaster-api/Data/Repositories/CommentRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using taskmaster_api.Data.Contexts; +using taskmaster_api.Data.Entities; +using taskmaster_api.Data.Repositories.Interface; + +namespace taskmaster_api.Data.Repositories +{ + public class CommentRepository : ICommentRepository + { + private readonly ApplicationDbContext _context; + + public CommentRepository(ApplicationDbContext context) + { + _context = context; + } + + public CommentEntity GetCommentById(int id) + { + return _context.Comments.Find(id); + } + + public IEnumerable GetAllComments() + { + return _context.Comments.ToList(); + } + + public CommentEntity CreateComment(CommentEntity comment) + { + _context.Comments.Add(comment); + _context.SaveChanges(); + return comment; + } + + public CommentEntity UpdateComment(int id, CommentEntity comment) + { + if (_context.Comments.Find(id) is CommentEntity oldComment) + { + comment.Id = id; + _context.Comments.Entry(oldComment).State = EntityState.Detached; + _context.Comments.Entry(comment).State = EntityState.Modified; + _context.SaveChanges(); + } + return comment; + } + + public int DeleteComment(int id) + { + var commentToDelete = _context.Comments.Find(id); + if (commentToDelete != null) + { + _context.Comments.Remove(commentToDelete); + _context.SaveChanges(); + return id; + } + return -1; + } + } +} diff --git a/taskmaster-api/Data/Repositories/Interface/ICommentRepository.cs b/taskmaster-api/Data/Repositories/Interface/ICommentRepository.cs new file mode 100644 index 0000000..f2ab0a1 --- /dev/null +++ b/taskmaster-api/Data/Repositories/Interface/ICommentRepository.cs @@ -0,0 +1,13 @@ +using taskmaster_api.Data.Entities; + +namespace taskmaster_api.Data.Repositories.Interface +{ + public interface ICommentRepository + { + CommentEntity GetCommentById(int id); + IEnumerable GetAllComments(); + CommentEntity CreateComment(CommentEntity comment); + CommentEntity UpdateComment(int id, CommentEntity comment); + int DeleteComment(int id); + } +} diff --git a/taskmaster-api/Migrations/20231211053644_InitialCreate.Designer.cs b/taskmaster-api/Migrations/20231211062434_InitialCreate.Designer.cs similarity index 85% rename from taskmaster-api/Migrations/20231211053644_InitialCreate.Designer.cs rename to taskmaster-api/Migrations/20231211062434_InitialCreate.Designer.cs index c2225c3..11b51f4 100644 --- a/taskmaster-api/Migrations/20231211053644_InitialCreate.Designer.cs +++ b/taskmaster-api/Migrations/20231211062434_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace taskmaster_api.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20231211053644_InitialCreate")] + [Migration("20231211062434_InitialCreate")] partial class InitialCreate { /// @@ -223,6 +223,40 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens", (string)null); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("TaskId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("TaskId"); + + b.HasIndex("UserId"); + + b.ToTable("Comments"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TaskEntity", b => { b.Property("Id") @@ -307,6 +341,25 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); + + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => + { + b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Task"); + + b.Navigation("User"); + }); #pragma warning restore 612, 618 } } diff --git a/taskmaster-api/Migrations/20231211053644_InitialCreate.cs b/taskmaster-api/Migrations/20231211062434_InitialCreate.cs similarity index 85% rename from taskmaster-api/Migrations/20231211053644_InitialCreate.cs rename to taskmaster-api/Migrations/20231211062434_InitialCreate.cs index df85bb3..5c198e8 100644 --- a/taskmaster-api/Migrations/20231211053644_InitialCreate.cs +++ b/taskmaster-api/Migrations/20231211062434_InitialCreate.cs @@ -174,6 +174,35 @@ protected override void Up(MigrationBuilder migrationBuilder) onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "Comments", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + TaskId = table.Column(type: "int", nullable: false), + Content = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Comments", x => x.Id); + table.ForeignKey( + name: "FK_Comments_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Comments_Tasks_TaskId", + column: x => x.TaskId, + principalTable: "Tasks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateIndex( name: "IX_AspNetRoleClaims_RoleId", table: "AspNetRoleClaims", @@ -212,6 +241,16 @@ protected override void Up(MigrationBuilder migrationBuilder) column: "NormalizedUserName", unique: true, filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_Comments_TaskId", + table: "Comments", + column: "TaskId"); + + migrationBuilder.CreateIndex( + name: "IX_Comments_UserId", + table: "Comments", + column: "UserId"); } /// @@ -233,13 +272,16 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "AspNetUserTokens"); migrationBuilder.DropTable( - name: "Tasks"); + name: "Comments"); migrationBuilder.DropTable( name: "AspNetRoles"); migrationBuilder.DropTable( name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "Tasks"); } } } diff --git a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs index df2fd24..bb88aaa 100644 --- a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs @@ -220,6 +220,40 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens", (string)null); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("TaskId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("TaskId"); + + b.HasIndex("UserId"); + + b.ToTable("Comments"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TaskEntity", b => { b.Property("Id") @@ -304,6 +338,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); + + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => + { + b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Task"); + + b.Navigation("User"); + }); #pragma warning restore 612, 618 } } diff --git a/taskmaster-api/Program.cs b/taskmaster-api/Program.cs index a2bcf9b..f00f98b 100644 --- a/taskmaster-api/Program.cs +++ b/taskmaster-api/Program.cs @@ -60,9 +60,11 @@ }); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/taskmaster-api/Services/CommentService.cs b/taskmaster-api/Services/CommentService.cs new file mode 100644 index 0000000..810c742 --- /dev/null +++ b/taskmaster-api/Services/CommentService.cs @@ -0,0 +1,109 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Repositories.Interface; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Services +{ + public class CommentService : ICommentService + { + private readonly ICommentRepository _commentRepository; + private readonly ILogger _logger; + + public CommentService(ICommentRepository commentRepository, ILogger logger) + { + _commentRepository = commentRepository; + _logger = logger; + } + + public ICoreActionResult GetCommentById(int id) + { + try + { + var comment = _commentRepository.GetCommentById(id); + if (comment == null) + { + _logger.LogInformation("Comment not found"); + return CoreActionResult.Failure("Comment not found", "NotFound"); + } + + return CoreActionResult.Success(comment.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult> GetAllComments() + { + try + { + var comments = _commentRepository.GetAllComments(); + return CoreActionResult>.Success(comments.Select(comment => comment.ToDto()).ToList()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult>.Exception(ex); + } + } + + public ICoreActionResult CreateComment(CommentDto commentDto) + { + try + { + var comment = commentDto.ToEntity(); + var newComment = _commentRepository.CreateComment(comment); + return CoreActionResult.Success(newComment.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UpdateComment(int id, CommentDto commentDto) + { + try + { + var comment = commentDto.ToEntity(); + var existingComment = _commentRepository.GetCommentById(id); + if (existingComment == null) + { + _logger.LogInformation("Comment not found"); + return CoreActionResult.Failure("Comment not found", "NotFound"); + } + + var updatedComment = _commentRepository.UpdateComment(id, comment); + return CoreActionResult.Success(updatedComment.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult DeleteComment(int id) + { + try + { + var deletedCommentId = _commentRepository.DeleteComment(id); + if (deletedCommentId == 0) + { + _logger.LogInformation("Comment not found"); + return CoreActionResult.Ignore("Comment not found", "NotFound"); + } + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + } +} diff --git a/taskmaster-api/Services/Interface/ICommentService.cs b/taskmaster-api/Services/Interface/ICommentService.cs new file mode 100644 index 0000000..02d1999 --- /dev/null +++ b/taskmaster-api/Services/Interface/ICommentService.cs @@ -0,0 +1,14 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; + +namespace taskmaster_api.Services.Interface +{ + public interface ICommentService + { + ICoreActionResult GetCommentById(int id); + ICoreActionResult> GetAllComments(); + ICoreActionResult CreateComment(CommentDto commentDto); + ICoreActionResult UpdateComment(int id, CommentDto commentDto); + ICoreActionResult DeleteComment(int id); + } +} diff --git a/taskmaster-api/Utilities/DtoHelpers.cs b/taskmaster-api/Utilities/DtoHelpers.cs deleted file mode 100644 index 0902ed2..0000000 --- a/taskmaster-api/Utilities/DtoHelpers.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace taskmaster_api.Utilities -{ - public static class DtoHelpers - { - public static TDestination ToEntity(TSource source) - where TDestination : new() - { - var destination = new TDestination(); - var sourceProperties = typeof(TSource).GetProperties(); - var destinationProperties = typeof(TDestination).GetProperties(); - - foreach (var sourceProp in sourceProperties) - { - var destinationProp = destinationProperties.FirstOrDefault(x => x.Name == sourceProp.Name); - - if (destinationProp != null && destinationProp.CanWrite) - { - var value = sourceProp.GetValue(source); - destinationProp.SetValue(destination, value); - } - } - - return destination; - } - } -} diff --git a/taskmaster-api/Utilities/EntityHelpers.cs b/taskmaster-api/Utilities/EntityHelpers.cs index 4779302..2db5e4e 100644 --- a/taskmaster-api/Utilities/EntityHelpers.cs +++ b/taskmaster-api/Utilities/EntityHelpers.cs @@ -22,6 +22,27 @@ public static TDestination ToDto(TSource source) return destination; } + + public static TDestination ToEntity(TSource source) + where TDestination : new() + { + var destination = new TDestination(); + var sourceProperties = typeof(TSource).GetProperties(); + var destinationProperties = typeof(TDestination).GetProperties(); + + foreach (var sourceProp in sourceProperties) + { + var destinationProp = destinationProperties.FirstOrDefault(x => x.Name == sourceProp.Name); + + if (destinationProp != null && destinationProp.CanWrite) + { + var value = sourceProp.GetValue(source); + destinationProp.SetValue(destination, value); + } + } + + return destination; + } } } From c238042acbdd0a97796c38cc90a0a2d29614f29b Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 15:57:43 +0800 Subject: [PATCH 2/8] - add attachment table and endpoints - implment file upload logic --- .gitignore | 5 +- .../Controllers/AttachmentController.cs | 55 +++++++ .../Data/Contexts/ApplicationDbContext.cs | 1 + taskmaster-api/Data/DTOs/AttachmentDto.cs | 32 +++- .../Data/DTOs/AttachmentUploadRequest.cs | 7 + .../Data/DTOs/AttachmentUploadResult.cs | 9 ++ .../Data/Entities/AttachmentEntity.cs | 35 ++++- .../Data/Repositories/AttachmentRepository.cs | 58 +++++++ .../Interface/IAttachmentRepository.cs | 13 ++ taskmaster-api/Program.cs | 3 + taskmaster-api/Services/AttachmentService.cs | 145 ++++++++++++++++++ .../Services/Interface/IAttachmentService.cs | 15 ++ 12 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 taskmaster-api/Controllers/AttachmentController.cs create mode 100644 taskmaster-api/Data/DTOs/AttachmentUploadRequest.cs create mode 100644 taskmaster-api/Data/DTOs/AttachmentUploadResult.cs create mode 100644 taskmaster-api/Data/Repositories/AttachmentRepository.cs create mode 100644 taskmaster-api/Data/Repositories/Interface/IAttachmentRepository.cs create mode 100644 taskmaster-api/Services/AttachmentService.cs create mode 100644 taskmaster-api/Services/Interface/IAttachmentService.cs diff --git a/.gitignore b/.gitignore index 9491a2f..0818570 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,7 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +# Upload foler +taskmaster-api/Uploads/ \ No newline at end of file diff --git a/taskmaster-api/Controllers/AttachmentController.cs b/taskmaster-api/Controllers/AttachmentController.cs new file mode 100644 index 0000000..a1df184 --- /dev/null +++ b/taskmaster-api/Controllers/AttachmentController.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class AttachmentController : ApplicationControllerBase + { + private readonly IAttachmentService _attachmentService; + + public AttachmentController(IAttachmentService attachmentService) + { + _attachmentService = attachmentService; + } + + [HttpGet] + public IActionResult GetAllAttachments() + { + return ToHttpResult>(_attachmentService.GetAllAttachments()); + } + + [HttpGet("{id}")] + public IActionResult GetAttachment(int id) + { + return ToHttpResult(_attachmentService.GetAttachmentById(id)); + } + + [HttpPost] + public IActionResult CreateAttachment(AttachmentDto attachmentDto) + { + return ToHttpResult(_attachmentService.CreateAttachment(attachmentDto)); + } + + [HttpPut("{id}")] + public IActionResult UpdateAttachment(int id, AttachmentDto attachmentDto) + { + return ToHttpResult(_attachmentService.UpdateAttachment(id, attachmentDto)); + } + + [HttpDelete("{id}")] + public IActionResult DeleteAttachment(int id) + { + return ToHttpResult(_attachmentService.DeleteAttachment(id)); + } + + [HttpPost("upload")] + public IActionResult Upload([FromForm] AttachmentUploadRequest request) + { + return ToHttpResult(_attachmentService.UploadFile(request)); + } + } +} diff --git a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs index cd58268..0ec26d6 100644 --- a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs +++ b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs @@ -13,5 +13,6 @@ public ApplicationDbContext(DbContextOptions options) : base(options) public virtual DbSet Tasks { get; set; } public virtual DbSet Comments { get; set; } + public virtual DbSet Attachments { get; set; } } } diff --git a/taskmaster-api/Data/DTOs/AttachmentDto.cs b/taskmaster-api/Data/DTOs/AttachmentDto.cs index 4c27fb9..f100303 100644 --- a/taskmaster-api/Data/DTOs/AttachmentDto.cs +++ b/taskmaster-api/Data/DTOs/AttachmentDto.cs @@ -1,6 +1,34 @@ -namespace taskmaster_api.Data.DTOs +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.Entities; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.DTOs { - public class AttachmentDto + public class AttachmentDto : IDto { + public int? Id { get; set; } + public string UserId { get; set; } + public int TaskId { get; set; } + public string FileName { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + + public AttachmentDto() + { + if (Id.HasValue) + { + UpdatedAt = DateTime.UtcNow; + } + else + { + CreatedAt = DateTime.UtcNow; + UpdatedAt = DateTime.UtcNow; + } + } + + public AttachmentEntity ToEntity() + { + return EntityHelpers.ToEntity(this); + } } } diff --git a/taskmaster-api/Data/DTOs/AttachmentUploadRequest.cs b/taskmaster-api/Data/DTOs/AttachmentUploadRequest.cs new file mode 100644 index 0000000..2f2fd1e --- /dev/null +++ b/taskmaster-api/Data/DTOs/AttachmentUploadRequest.cs @@ -0,0 +1,7 @@ +namespace taskmaster_api.Data.DTOs +{ + public class AttachmentUploadRequest + { + public IFormFile File { get; set; } + } +} diff --git a/taskmaster-api/Data/DTOs/AttachmentUploadResult.cs b/taskmaster-api/Data/DTOs/AttachmentUploadResult.cs new file mode 100644 index 0000000..52c52c9 --- /dev/null +++ b/taskmaster-api/Data/DTOs/AttachmentUploadResult.cs @@ -0,0 +1,9 @@ +namespace taskmaster_api.Data.DTOs +{ + public class AttachmentUploadResult + { + public bool Success { get; set; } + public string FilePath { get; set; } + public string FileName { get; set; } + } +} diff --git a/taskmaster-api/Data/Entities/AttachmentEntity.cs b/taskmaster-api/Data/Entities/AttachmentEntity.cs index c75bc61..442ffdd 100644 --- a/taskmaster-api/Data/Entities/AttachmentEntity.cs +++ b/taskmaster-api/Data/Entities/AttachmentEntity.cs @@ -1,6 +1,37 @@ -namespace taskmaster_api.Data.Entities +using Microsoft.AspNetCore.Identity; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Entities.Interface; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.Entities { - public class AttachmentEntity + public class AttachmentEntity : IEntity { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int? Id { get; set; } + + [Required] + public string UserId { get; set; } + public IdentityUser User { get; set; } + + [Required] + public int TaskId { get; set; } + public TaskEntity Task { get; set; } + + [Required] + [StringLength(255)] + public string FileName { get; set; } + + public DateTime CreatedAt { get; set; } + + public DateTime UpdatedAt { get; set; } + + public AttachmentDto ToDto() + { + return EntityHelpers.ToDto(this); + } } } diff --git a/taskmaster-api/Data/Repositories/AttachmentRepository.cs b/taskmaster-api/Data/Repositories/AttachmentRepository.cs new file mode 100644 index 0000000..fe18329 --- /dev/null +++ b/taskmaster-api/Data/Repositories/AttachmentRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using taskmaster_api.Data.Contexts; +using taskmaster_api.Data.Entities; +using taskmaster_api.Data.Repositories.Interface; + +namespace taskmaster_api.Data.Repositories +{ + public class AttachmentRepository : IAttachmentRepository + { + private readonly ApplicationDbContext _context; + + public AttachmentRepository(ApplicationDbContext context) + { + _context = context; + } + + public AttachmentEntity GetAttachmentById(int id) + { + return _context.Attachments.Find(id); + } + + public IEnumerable GetAllAttachments() + { + return _context.Attachments.ToList(); + } + + public AttachmentEntity CreateAttachment(AttachmentEntity attachment) + { + _context.Attachments.Add(attachment); + _context.SaveChanges(); + return attachment; + } + + public AttachmentEntity UpdateAttachment(int id, AttachmentEntity attachment) + { + if (_context.Attachments.Find(id) is AttachmentEntity oldAttachment) + { + attachment.Id = id; + _context.Attachments.Entry(oldAttachment).State = EntityState.Detached; + _context.Attachments.Entry(attachment).State = EntityState.Modified; + _context.SaveChanges(); + } + return attachment; + } + + public int DeleteAttachment(int id) + { + var attachmentToDelete = _context.Attachments.Find(id); + if (attachmentToDelete != null) + { + _context.Attachments.Remove(attachmentToDelete); + _context.SaveChanges(); + return id; + } + return -1; + } + } +} diff --git a/taskmaster-api/Data/Repositories/Interface/IAttachmentRepository.cs b/taskmaster-api/Data/Repositories/Interface/IAttachmentRepository.cs new file mode 100644 index 0000000..d6c92a6 --- /dev/null +++ b/taskmaster-api/Data/Repositories/Interface/IAttachmentRepository.cs @@ -0,0 +1,13 @@ +using taskmaster_api.Data.Entities; + +namespace taskmaster_api.Data.Repositories.Interface +{ + public interface IAttachmentRepository + { + AttachmentEntity GetAttachmentById(int id); + IEnumerable GetAllAttachments(); + AttachmentEntity CreateAttachment(AttachmentEntity attachment); + AttachmentEntity UpdateAttachment(int id, AttachmentEntity attachment); + int DeleteAttachment(int id); + } +} diff --git a/taskmaster-api/Program.cs b/taskmaster-api/Program.cs index f00f98b..479c08c 100644 --- a/taskmaster-api/Program.cs +++ b/taskmaster-api/Program.cs @@ -61,10 +61,13 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/taskmaster-api/Services/AttachmentService.cs b/taskmaster-api/Services/AttachmentService.cs new file mode 100644 index 0000000..4fbb9d3 --- /dev/null +++ b/taskmaster-api/Services/AttachmentService.cs @@ -0,0 +1,145 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Repositories.Interface; +using taskmaster_api.Services.Interface; +using Microsoft.AspNetCore.Hosting; + +namespace taskmaster_api.Services +{ + public class AttachmentService : IAttachmentService + { + private readonly IAttachmentRepository _attachmentRepository; + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly ILogger _logger; + + public AttachmentService(IAttachmentRepository attachmentRepository, IWebHostEnvironment webHostEnvironment, ILogger logger) + { + _attachmentRepository = attachmentRepository; + _webHostEnvironment = webHostEnvironment; + _logger = logger; + } + + public ICoreActionResult GetAttachmentById(int id) + { + try + { + var attachment = _attachmentRepository.GetAttachmentById(id); + if (attachment == null) + { + _logger.LogInformation("Attachment not found"); + return CoreActionResult.Failure("Attachment not found", "NotFound"); + } + + return CoreActionResult.Success(attachment.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult> GetAllAttachments() + { + try + { + var attachments = _attachmentRepository.GetAllAttachments(); + return CoreActionResult>.Success(attachments.Select(attachment => attachment.ToDto()).ToList()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult>.Exception(ex); + } + } + + public ICoreActionResult CreateAttachment(AttachmentDto attachmentDto) + { + try + { + var attachment = attachmentDto.ToEntity(); + var newAttachment = _attachmentRepository.CreateAttachment(attachment); + return CoreActionResult.Success(newAttachment.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UpdateAttachment(int id, AttachmentDto attachmentDto) + { + try + { + var attachment = attachmentDto.ToEntity(); + var existingAttachment = _attachmentRepository.GetAttachmentById(id); + if (existingAttachment == null) + { + _logger.LogInformation("Attachment not found"); + return CoreActionResult.Failure("Attachment not found", "NotFound"); + } + + var updatedAttachment = _attachmentRepository.UpdateAttachment(id, attachment); + return CoreActionResult.Success(updatedAttachment.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult DeleteAttachment(int id) + { + try + { + var deletedAttachmentId = _attachmentRepository.DeleteAttachment(id); + if (deletedAttachmentId == 0) + { + _logger.LogInformation("Attachment not found"); + return CoreActionResult.Ignore("Attachment not found", "NotFound"); + } + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UploadFile(AttachmentUploadRequest request) + { + if (request == null || request.File == null || request.File.Length <= 0) + { + return CoreActionResult.Failure("Invalid file"); + } + + try + { + // Create a unique filename to avoid overwriting existing files + var fileName = Guid.NewGuid().ToString() + Path.GetExtension(request.File.FileName); + + // Combine the unique filename with the storage path + var filePath = Path.Combine(_webHostEnvironment.ContentRootPath, "Uploads", fileName); + + // Ensure the directory exists + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + + // Save the file to the specified path + using (var stream = new FileStream(filePath, FileMode.Create)) + { + request.File.CopyTo(stream); + } + + return CoreActionResult.Success(new AttachmentUploadResult { Success = true, FilePath = filePath, FileName = fileName }); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Success(new AttachmentUploadResult { Success = false }); + } + } + } +} diff --git a/taskmaster-api/Services/Interface/IAttachmentService.cs b/taskmaster-api/Services/Interface/IAttachmentService.cs new file mode 100644 index 0000000..d67206d --- /dev/null +++ b/taskmaster-api/Services/Interface/IAttachmentService.cs @@ -0,0 +1,15 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; + +namespace taskmaster_api.Services.Interface +{ + public interface IAttachmentService + { + ICoreActionResult GetAttachmentById(int id); + ICoreActionResult> GetAllAttachments(); + ICoreActionResult CreateAttachment(AttachmentDto attachmentDto); + ICoreActionResult UpdateAttachment(int id, AttachmentDto attachmentDto); + ICoreActionResult DeleteAttachment(int id); + ICoreActionResult UploadFile(AttachmentUploadRequest request); + } +} From ff349af05dc4aa29bf17cf54c2b824ad3cc0b96a Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 15:58:54 +0800 Subject: [PATCH 3/8] set authorization to AttachmentController --- taskmaster-api/Controllers/AttachmentController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/taskmaster-api/Controllers/AttachmentController.cs b/taskmaster-api/Controllers/AttachmentController.cs index a1df184..ed52c97 100644 --- a/taskmaster-api/Controllers/AttachmentController.cs +++ b/taskmaster-api/Controllers/AttachmentController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using taskmaster_api.Data.DTOs; using taskmaster_api.Services.Interface; @@ -7,6 +8,7 @@ namespace taskmaster_api.Controllers { [Route("api/[controller]")] [ApiController] + [Authorize] public class AttachmentController : ApplicationControllerBase { private readonly IAttachmentService _attachmentService; From e21a9bf5019e3a3994b25704e7373223772b9e06 Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 16:23:50 +0800 Subject: [PATCH 4/8] add tag table and endpoints --- taskmaster-api/Controllers/TagController.cs | 49 ++++++++ .../Data/Contexts/ApplicationDbContext.cs | 1 + taskmaster-api/Data/DTOs/TagDto.cs | 19 ++- taskmaster-api/Data/Entities/TagEntity.cs | 23 +++- .../Repositories/Interface/ITagRepository.cs | 13 +++ .../Data/Repositories/TagRepository.cs | 58 ++++++++++ ... 20231211082219_InitialCreate.Designer.cs} | 74 +++++++++++- ...ate.cs => 20231211082219_InitialCreate.cs} | 58 ++++++++++ .../ApplicationDbContextModelSnapshot.cs | 72 ++++++++++++ taskmaster-api/Program.cs | 2 + .../Services/Interface/ITagService.cs | 14 +++ taskmaster-api/Services/TagService.cs | 109 ++++++++++++++++++ 12 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 taskmaster-api/Controllers/TagController.cs create mode 100644 taskmaster-api/Data/Repositories/Interface/ITagRepository.cs create mode 100644 taskmaster-api/Data/Repositories/TagRepository.cs rename taskmaster-api/Migrations/{20231211062434_InitialCreate.Designer.cs => 20231211082219_InitialCreate.Designer.cs} (83%) rename taskmaster-api/Migrations/{20231211062434_InitialCreate.cs => 20231211082219_InitialCreate.cs} (83%) create mode 100644 taskmaster-api/Services/Interface/ITagService.cs create mode 100644 taskmaster-api/Services/TagService.cs diff --git a/taskmaster-api/Controllers/TagController.cs b/taskmaster-api/Controllers/TagController.cs new file mode 100644 index 0000000..e76360b --- /dev/null +++ b/taskmaster-api/Controllers/TagController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TagController : ApplicationControllerBase + { + private readonly ITagService _tagService; + + public TagController(ITagService tagService) + { + _tagService = tagService; + } + + [HttpGet] + public IActionResult GetAllTags() + { + return ToHttpResult>(_tagService.GetAllTags()); + } + + [HttpGet("{id}")] + public IActionResult GetTag(int id) + { + return ToHttpResult(_tagService.GetTagById(id)); + } + + [HttpPost] + public IActionResult CreateTag(TagDto tagDto) + { + return ToHttpResult(_tagService.CreateTag(tagDto)); + } + + [HttpPut("{id}")] + public IActionResult UpdateTag(int id, TagDto tagDto) + { + return ToHttpResult(_tagService.UpdateTag(id, tagDto)); + } + + [HttpDelete("{id}")] + public IActionResult DeleteTag(int id) + { + return ToHttpResult(_tagService.DeleteTag(id)); + } + } +} diff --git a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs index 0ec26d6..0c4f7d0 100644 --- a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs +++ b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs @@ -14,5 +14,6 @@ public ApplicationDbContext(DbContextOptions options) : base(options) public virtual DbSet Tasks { get; set; } public virtual DbSet Comments { get; set; } public virtual DbSet Attachments { get; set; } + public virtual DbSet Tags { get; set; } } } diff --git a/taskmaster-api/Data/DTOs/TagDto.cs b/taskmaster-api/Data/DTOs/TagDto.cs index 7115e14..00ae084 100644 --- a/taskmaster-api/Data/DTOs/TagDto.cs +++ b/taskmaster-api/Data/DTOs/TagDto.cs @@ -1,6 +1,21 @@ -namespace taskmaster_api.Data.DTOs +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.Entities; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.DTOs { - public class TagDto + public class TagDto : IDto { + public int? Id { get; set; } + public string Name { get; set; } + + public TagDto() + { + } + + public TagEntity ToEntity() + { + return EntityHelpers.ToEntity(this); + } } } diff --git a/taskmaster-api/Data/Entities/TagEntity.cs b/taskmaster-api/Data/Entities/TagEntity.cs index 269a422..c23d137 100644 --- a/taskmaster-api/Data/Entities/TagEntity.cs +++ b/taskmaster-api/Data/Entities/TagEntity.cs @@ -1,6 +1,25 @@ -namespace taskmaster_api.Data.Entities +using Microsoft.AspNetCore.Identity; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Utilities; +using taskmaster_api.Data.Entities.Interface; + +namespace taskmaster_api.Data.Entities { - public class TagEntity + public class TagEntity : IEntity { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int? Id { get; set; } + + [Required] + [StringLength(50)] + public string Name { get; set; } + + public TagDto ToDto() + { + return EntityHelpers.ToDto(this); + } } } diff --git a/taskmaster-api/Data/Repositories/Interface/ITagRepository.cs b/taskmaster-api/Data/Repositories/Interface/ITagRepository.cs new file mode 100644 index 0000000..a7563ad --- /dev/null +++ b/taskmaster-api/Data/Repositories/Interface/ITagRepository.cs @@ -0,0 +1,13 @@ +using taskmaster_api.Data.Entities; + +namespace taskmaster_api.Data.Repositories.Interface +{ + public interface ITagRepository + { + TagEntity GetTagById(int id); + IEnumerable GetAllTags(); + TagEntity CreateTag(TagEntity tag); + TagEntity UpdateTag(int id, TagEntity tag); + int DeleteTag(int id); + } +} diff --git a/taskmaster-api/Data/Repositories/TagRepository.cs b/taskmaster-api/Data/Repositories/TagRepository.cs new file mode 100644 index 0000000..237e9f6 --- /dev/null +++ b/taskmaster-api/Data/Repositories/TagRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using taskmaster_api.Data.Contexts; +using taskmaster_api.Data.Entities; +using taskmaster_api.Data.Repositories.Interface; + +namespace taskmaster_api.Data.Repositories +{ + public class TagRepository : ITagRepository + { + private readonly ApplicationDbContext _context; + + public TagRepository(ApplicationDbContext context) + { + _context = context; + } + + public TagEntity GetTagById(int id) + { + return _context.Tags.Find(id); + } + + public IEnumerable GetAllTags() + { + return _context.Tags.ToList(); + } + + public TagEntity CreateTag(TagEntity tag) + { + _context.Tags.Add(tag); + _context.SaveChanges(); + return tag; + } + + public TagEntity UpdateTag(int id, TagEntity tag) + { + if (_context.Tags.Find(id) is TagEntity oldTag) + { + tag.Id = id; + _context.Tags.Entry(oldTag).State = EntityState.Detached; + _context.Tags.Entry(tag).State = EntityState.Modified; + _context.SaveChanges(); + } + return tag; + } + + public int DeleteTag(int id) + { + var tagToDelete = _context.Tags.Find(id); + if (tagToDelete != null) + { + _context.Tags.Remove(tagToDelete); + _context.SaveChanges(); + return id; + } + return -1; + } + } +} diff --git a/taskmaster-api/Migrations/20231211062434_InitialCreate.Designer.cs b/taskmaster-api/Migrations/20231211082219_InitialCreate.Designer.cs similarity index 83% rename from taskmaster-api/Migrations/20231211062434_InitialCreate.Designer.cs rename to taskmaster-api/Migrations/20231211082219_InitialCreate.Designer.cs index 11b51f4..422acc0 100644 --- a/taskmaster-api/Migrations/20231211062434_InitialCreate.Designer.cs +++ b/taskmaster-api/Migrations/20231211082219_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace taskmaster_api.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20231211062434_InitialCreate")] + [Migration("20231211082219_InitialCreate")] partial class InitialCreate { /// @@ -223,6 +223,41 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens", (string)null); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TaskId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("TaskId"); + + b.HasIndex("UserId"); + + b.ToTable("Attachments"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => { b.Property("Id") @@ -257,6 +292,24 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Comments"); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TagEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TaskEntity", b => { b.Property("Id") @@ -342,6 +395,25 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => + { + b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => { b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") diff --git a/taskmaster-api/Migrations/20231211062434_InitialCreate.cs b/taskmaster-api/Migrations/20231211082219_InitialCreate.cs similarity index 83% rename from taskmaster-api/Migrations/20231211062434_InitialCreate.cs rename to taskmaster-api/Migrations/20231211082219_InitialCreate.cs index 5c198e8..1705098 100644 --- a/taskmaster-api/Migrations/20231211062434_InitialCreate.cs +++ b/taskmaster-api/Migrations/20231211082219_InitialCreate.cs @@ -50,6 +50,19 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_AspNetUsers", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Tags", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tags", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Tasks", columns: table => new @@ -174,6 +187,35 @@ protected override void Up(MigrationBuilder migrationBuilder) onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "Attachments", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + TaskId = table.Column(type: "int", nullable: false), + FileName = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Attachments", x => x.Id); + table.ForeignKey( + name: "FK_Attachments_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Attachments_Tasks_TaskId", + column: x => x.TaskId, + principalTable: "Tasks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "Comments", columns: table => new @@ -242,6 +284,16 @@ protected override void Up(MigrationBuilder migrationBuilder) unique: true, filter: "[NormalizedUserName] IS NOT NULL"); + migrationBuilder.CreateIndex( + name: "IX_Attachments_TaskId", + table: "Attachments", + column: "TaskId"); + + migrationBuilder.CreateIndex( + name: "IX_Attachments_UserId", + table: "Attachments", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_Comments_TaskId", table: "Comments", @@ -271,9 +323,15 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "AspNetUserTokens"); + migrationBuilder.DropTable( + name: "Attachments"); + migrationBuilder.DropTable( name: "Comments"); + migrationBuilder.DropTable( + name: "Tags"); + migrationBuilder.DropTable( name: "AspNetRoles"); diff --git a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs index bb88aaa..81d688d 100644 --- a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs @@ -220,6 +220,41 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens", (string)null); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TaskId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("TaskId"); + + b.HasIndex("UserId"); + + b.ToTable("Attachments"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => { b.Property("Id") @@ -254,6 +289,24 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Comments"); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TagEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TaskEntity", b => { b.Property("Id") @@ -339,6 +392,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => + { + b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.CommentEntity", b => { b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") diff --git a/taskmaster-api/Program.cs b/taskmaster-api/Program.cs index 479c08c..6c5d8ce 100644 --- a/taskmaster-api/Program.cs +++ b/taskmaster-api/Program.cs @@ -62,12 +62,14 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/taskmaster-api/Services/Interface/ITagService.cs b/taskmaster-api/Services/Interface/ITagService.cs new file mode 100644 index 0000000..795f981 --- /dev/null +++ b/taskmaster-api/Services/Interface/ITagService.cs @@ -0,0 +1,14 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; + +namespace taskmaster_api.Services.Interface +{ + public interface ITagService + { + ICoreActionResult GetTagById(int id); + ICoreActionResult> GetAllTags(); + ICoreActionResult CreateTag(TagDto tagDto); + ICoreActionResult UpdateTag(int id, TagDto tagDto); + ICoreActionResult DeleteTag(int id); + } +} diff --git a/taskmaster-api/Services/TagService.cs b/taskmaster-api/Services/TagService.cs new file mode 100644 index 0000000..5a3db51 --- /dev/null +++ b/taskmaster-api/Services/TagService.cs @@ -0,0 +1,109 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Repositories.Interface; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Services +{ + public class TagService : ITagService + { + private readonly ITagRepository _tagRepository; + private readonly ILogger _logger; + + public TagService(ITagRepository tagRepository, ILogger logger) + { + _tagRepository = tagRepository; + _logger = logger; + } + + public ICoreActionResult GetTagById(int id) + { + try + { + var tag = _tagRepository.GetTagById(id); + if (tag == null) + { + _logger.LogInformation("Tag not found"); + return CoreActionResult.Failure("Tag not found", "NotFound"); + } + + return CoreActionResult.Success(tag.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult> GetAllTags() + { + try + { + var tags = _tagRepository.GetAllTags(); + return CoreActionResult>.Success(tags.Select(tag => tag.ToDto()).ToList()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult>.Exception(ex); + } + } + + public ICoreActionResult CreateTag(TagDto tagDto) + { + try + { + var tag = tagDto.ToEntity(); + var newTag = _tagRepository.CreateTag(tag); + return CoreActionResult.Success(newTag.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UpdateTag(int id, TagDto tagDto) + { + try + { + var tag = tagDto.ToEntity(); + var existingTag = _tagRepository.GetTagById(id); + if (existingTag == null) + { + _logger.LogInformation("Tag not found"); + return CoreActionResult.Failure("Tag not found", "NotFound"); + } + + var updatedTag = _tagRepository.UpdateTag(id, tag); + return CoreActionResult.Success(updatedTag.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult DeleteTag(int id) + { + try + { + var deletedTagId = _tagRepository.DeleteTag(id); + if (deletedTagId == 0) + { + _logger.LogInformation("Tag not found"); + return CoreActionResult.Ignore("Tag not found", "NotFound"); + } + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + } +} From 4cfa276a57d132f20b75ab083a9a0f8b959541dc Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 16:44:39 +0800 Subject: [PATCH 5/8] add table and endpoints for activity log --- .../Controllers/ActivityLogController.cs | 49 +++++++ .../Data/Contexts/ApplicationDbContext.cs | 1 + taskmaster-api/Data/DTOs/ActivityLogDto.cs | 25 +++- .../Data/Entities/ActivityLogEntity.cs | 29 +++- .../Repositories/ActivityLogRepository.cs | 58 ++++++++ .../Interface/IActivityLogRepository.cs | 13 ++ ... 20231211083537_InitialCreate.Designer.cs} | 40 +++++- ...ate.cs => 20231211083537_InitialCreate.cs} | 29 ++++ .../ApplicationDbContextModelSnapshot.cs | 38 +++++ taskmaster-api/Program.cs | 2 + taskmaster-api/Services/ActivityLogService.cs | 130 ++++++++++++++++++ .../Services/Interface/IActivityLogService.cs | 15 ++ 12 files changed, 424 insertions(+), 5 deletions(-) create mode 100644 taskmaster-api/Controllers/ActivityLogController.cs create mode 100644 taskmaster-api/Data/Repositories/ActivityLogRepository.cs create mode 100644 taskmaster-api/Data/Repositories/Interface/IActivityLogRepository.cs rename taskmaster-api/Migrations/{20231211082219_InitialCreate.Designer.cs => 20231211083537_InitialCreate.Designer.cs} (91%) rename taskmaster-api/Migrations/{20231211082219_InitialCreate.cs => 20231211083537_InitialCreate.cs} (92%) create mode 100644 taskmaster-api/Services/ActivityLogService.cs create mode 100644 taskmaster-api/Services/Interface/IActivityLogService.cs diff --git a/taskmaster-api/Controllers/ActivityLogController.cs b/taskmaster-api/Controllers/ActivityLogController.cs new file mode 100644 index 0000000..54aa2e0 --- /dev/null +++ b/taskmaster-api/Controllers/ActivityLogController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ActivityLogController : ApplicationControllerBase + { + private readonly IActivityLogService _activityLogService; + + public ActivityLogController(IActivityLogService activityLogService) + { + _activityLogService = activityLogService; + } + + [HttpGet] + public IActionResult GetAllActivityLogs() + { + return ToHttpResult>(_activityLogService.GetAllActivityLogs()); + } + + [HttpGet("{id}")] + public IActionResult GetActivityLog(int id) + { + return ToHttpResult(_activityLogService.GetActivityLogById(id)); + } + + [HttpPost] + public IActionResult CreateActivityLog(ActivityLogDto activityLogDto) + { + return ToHttpResult(_activityLogService.CreateActivityLog(activityLogDto)); + } + + [HttpPut("{id}")] + public IActionResult UpdateActivityLog(int id, ActivityLogDto activityLogDto) + { + return ToHttpResult(_activityLogService.UpdateActivityLog(id, activityLogDto)); + } + + [HttpDelete("{id}")] + public IActionResult DeleteActivityLog(int id) + { + return ToHttpResult(_activityLogService.DeleteActivityLog(id)); + } + } +} diff --git a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs index 0c4f7d0..52c2131 100644 --- a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs +++ b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs @@ -15,5 +15,6 @@ public ApplicationDbContext(DbContextOptions options) : base(options) public virtual DbSet Comments { get; set; } public virtual DbSet Attachments { get; set; } public virtual DbSet Tags { get; set; } + public virtual DbSet ActivityLogs { get; set; } } } diff --git a/taskmaster-api/Data/DTOs/ActivityLogDto.cs b/taskmaster-api/Data/DTOs/ActivityLogDto.cs index de3696a..57dffef 100644 --- a/taskmaster-api/Data/DTOs/ActivityLogDto.cs +++ b/taskmaster-api/Data/DTOs/ActivityLogDto.cs @@ -1,6 +1,27 @@ -namespace taskmaster_api.Data.DTOs +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.Entities; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.DTOs { - public class ActivityLogDto + public class ActivityLogDto : IDto { + public int? Id { get; set; } + public string UserId { get; set; } + public string Action { get; set; } + public DateTime CreatedAt { get; set; } + + public ActivityLogDto() + { + if (!Id.HasValue) + { + CreatedAt = DateTime.UtcNow; + } + } + + public ActivityLogEntity ToEntity() + { + return EntityHelpers.ToEntity(this); + } } } diff --git a/taskmaster-api/Data/Entities/ActivityLogEntity.cs b/taskmaster-api/Data/Entities/ActivityLogEntity.cs index 226fa57..30848e0 100644 --- a/taskmaster-api/Data/Entities/ActivityLogEntity.cs +++ b/taskmaster-api/Data/Entities/ActivityLogEntity.cs @@ -1,6 +1,31 @@ -namespace taskmaster_api.Data.Entities +using Microsoft.AspNetCore.Identity; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Utilities; +using taskmaster_api.Data.Entities.Interface; + +namespace taskmaster_api.Data.Entities { - public class ActivityLogEntity + public class ActivityLogEntity : IEntity { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int? Id { get; set; } + + [Required] + public string UserId { get; set; } + public IdentityUser User { get; set; } + + [Required] + [StringLength(100)] + public string Action { get; set; } + + public DateTime CreatedAt { get; set; } + + public ActivityLogDto ToDto() + { + return EntityHelpers.ToDto(this); + } } } diff --git a/taskmaster-api/Data/Repositories/ActivityLogRepository.cs b/taskmaster-api/Data/Repositories/ActivityLogRepository.cs new file mode 100644 index 0000000..2e3c33c --- /dev/null +++ b/taskmaster-api/Data/Repositories/ActivityLogRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using taskmaster_api.Data.Contexts; +using taskmaster_api.Data.Entities; +using taskmaster_api.Data.Repositories.Interface; + +namespace taskmaster_api.Data.Repositories +{ + public class ActivityLogRepository : IActivityLogRepository + { + private readonly ApplicationDbContext _context; + + public ActivityLogRepository(ApplicationDbContext context) + { + _context = context; + } + + public ActivityLogEntity GetActivityLogById(int id) + { + return _context.ActivityLogs.Find(id); + } + + public IEnumerable GetAllActivityLogs() + { + return _context.ActivityLogs.ToList(); + } + + public ActivityLogEntity CreateActivityLog(ActivityLogEntity activityLog) + { + _context.ActivityLogs.Add(activityLog); + _context.SaveChanges(); + return activityLog; + } + + public ActivityLogEntity UpdateActivityLog(int id, ActivityLogEntity activityLog) + { + if (_context.ActivityLogs.Find(id) is ActivityLogEntity oldActivityLog) + { + activityLog.Id = id; + _context.ActivityLogs.Entry(oldActivityLog).State = EntityState.Detached; + _context.ActivityLogs.Entry(activityLog).State = EntityState.Modified; + _context.SaveChanges(); + } + return activityLog; + } + + public int DeleteActivityLog(int id) + { + var activityLogToDelete = _context.ActivityLogs.Find(id); + if (activityLogToDelete != null) + { + _context.ActivityLogs.Remove(activityLogToDelete); + _context.SaveChanges(); + return id; + } + return -1; + } + } +} diff --git a/taskmaster-api/Data/Repositories/Interface/IActivityLogRepository.cs b/taskmaster-api/Data/Repositories/Interface/IActivityLogRepository.cs new file mode 100644 index 0000000..fbf0941 --- /dev/null +++ b/taskmaster-api/Data/Repositories/Interface/IActivityLogRepository.cs @@ -0,0 +1,13 @@ +using taskmaster_api.Data.Entities; + +namespace taskmaster_api.Data.Repositories.Interface +{ + public interface IActivityLogRepository + { + ActivityLogEntity GetActivityLogById(int id); + IEnumerable GetAllActivityLogs(); + ActivityLogEntity CreateActivityLog(ActivityLogEntity activityLog); + ActivityLogEntity UpdateActivityLog(int id, ActivityLogEntity activityLog); + int DeleteActivityLog(int id); + } +} diff --git a/taskmaster-api/Migrations/20231211082219_InitialCreate.Designer.cs b/taskmaster-api/Migrations/20231211083537_InitialCreate.Designer.cs similarity index 91% rename from taskmaster-api/Migrations/20231211082219_InitialCreate.Designer.cs rename to taskmaster-api/Migrations/20231211083537_InitialCreate.Designer.cs index 422acc0..d63ccfe 100644 --- a/taskmaster-api/Migrations/20231211082219_InitialCreate.Designer.cs +++ b/taskmaster-api/Migrations/20231211083537_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace taskmaster_api.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20231211082219_InitialCreate")] + [Migration("20231211083537_InitialCreate")] partial class InitialCreate { /// @@ -223,6 +223,33 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens", (string)null); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.ActivityLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ActivityLogs"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => { b.Property("Id") @@ -395,6 +422,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.ActivityLogEntity", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => { b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") diff --git a/taskmaster-api/Migrations/20231211082219_InitialCreate.cs b/taskmaster-api/Migrations/20231211083537_InitialCreate.cs similarity index 92% rename from taskmaster-api/Migrations/20231211082219_InitialCreate.cs rename to taskmaster-api/Migrations/20231211083537_InitialCreate.cs index 1705098..da79b04 100644 --- a/taskmaster-api/Migrations/20231211082219_InitialCreate.cs +++ b/taskmaster-api/Migrations/20231211083537_InitialCreate.cs @@ -102,6 +102,27 @@ protected override void Up(MigrationBuilder migrationBuilder) onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "ActivityLogs", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + Action = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ActivityLogs", x => x.Id); + table.ForeignKey( + name: "FK_ActivityLogs_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AspNetUserClaims", columns: table => new @@ -245,6 +266,11 @@ protected override void Up(MigrationBuilder migrationBuilder) onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateIndex( + name: "IX_ActivityLogs_UserId", + table: "ActivityLogs", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AspNetRoleClaims_RoleId", table: "AspNetRoleClaims", @@ -308,6 +334,9 @@ protected override void Up(MigrationBuilder migrationBuilder) /// protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "ActivityLogs"); + migrationBuilder.DropTable( name: "AspNetRoleClaims"); diff --git a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs index 81d688d..8fdeedc 100644 --- a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs @@ -220,6 +220,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AspNetUserTokens", (string)null); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.ActivityLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ActivityLogs"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => { b.Property("Id") @@ -392,6 +419,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.ActivityLogEntity", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.AttachmentEntity", b => { b.HasOne("taskmaster_api.Data.Entities.TaskEntity", "Task") diff --git a/taskmaster-api/Program.cs b/taskmaster-api/Program.cs index 6c5d8ce..71831d3 100644 --- a/taskmaster-api/Program.cs +++ b/taskmaster-api/Program.cs @@ -63,6 +63,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -70,6 +71,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/taskmaster-api/Services/ActivityLogService.cs b/taskmaster-api/Services/ActivityLogService.cs new file mode 100644 index 0000000..7a3fa3a --- /dev/null +++ b/taskmaster-api/Services/ActivityLogService.cs @@ -0,0 +1,130 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Repositories.Interface; +using taskmaster_api.Services.Interface; +using Microsoft.EntityFrameworkCore; + +namespace taskmaster_api.Services +{ + public class ActivityLogService : IActivityLogService + { + private readonly IActivityLogRepository _activityLogRepository; + private readonly ILogger _logger; + + public ActivityLogService(IActivityLogRepository activityLogRepository, ILogger logger) + { + _activityLogRepository = activityLogRepository; + _logger = logger; + } + + public ICoreActionResult GetActivityLogById(int id) + { + try + { + var activityLog = _activityLogRepository.GetActivityLogById(id); + if (activityLog == null) + { + _logger.LogInformation("ActivityLog not found"); + return CoreActionResult.Failure("ActivityLog not found", "NotFound"); + } + + return CoreActionResult.Success(activityLog.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult> GetAllActivityLogs() + { + try + { + var activityLogs = _activityLogRepository.GetAllActivityLogs(); + return CoreActionResult>.Success(activityLogs.Select(activityLog => activityLog.ToDto()).ToList()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult>.Exception(ex); + } + } + + public ICoreActionResult CreateActivityLog(ActivityLogDto activityLogDto) + { + try + { + var activityLog = activityLogDto.ToEntity(); + var newActivityLog = _activityLogRepository.CreateActivityLog(activityLog); + return CoreActionResult.Success(newActivityLog.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UpdateActivityLog(int id, ActivityLogDto activityLogDto) + { + try + { + var activityLog = activityLogDto.ToEntity(); + var existingActivityLog = _activityLogRepository.GetActivityLogById(id); + if (existingActivityLog == null) + { + _logger.LogInformation("ActivityLog not found"); + return CoreActionResult.Failure("ActivityLog not found", "NotFound"); + } + + var updatedActivityLog = _activityLogRepository.UpdateActivityLog(id, activityLog); + return CoreActionResult.Success(updatedActivityLog.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult DeleteActivityLog(int id) + { + try + { + var deletedActivityLogId = _activityLogRepository.DeleteActivityLog(id); + if (deletedActivityLogId == 0) + { + _logger.LogInformation("ActivityLog not found"); + return CoreActionResult.Ignore("ActivityLog not found", "NotFound"); + } + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult LogActivity(string userId, string action) + { + try + { + var activityLog = new Data.Entities.ActivityLogEntity + { + UserId = userId, + Action = action, + CreatedAt = DateTime.UtcNow + }; + var newActivityLog = _activityLogRepository.CreateActivityLog(activityLog); + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + } +} diff --git a/taskmaster-api/Services/Interface/IActivityLogService.cs b/taskmaster-api/Services/Interface/IActivityLogService.cs new file mode 100644 index 0000000..ef877d1 --- /dev/null +++ b/taskmaster-api/Services/Interface/IActivityLogService.cs @@ -0,0 +1,15 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; + +namespace taskmaster_api.Services.Interface +{ + public interface IActivityLogService + { + ICoreActionResult GetActivityLogById(int id); + ICoreActionResult> GetAllActivityLogs(); + ICoreActionResult CreateActivityLog(ActivityLogDto activityLogDto); + ICoreActionResult UpdateActivityLog(int id, ActivityLogDto activityLogDto); + ICoreActionResult DeleteActivityLog(int id); + ICoreActionResult LogActivity(string userId, string action); + } +} From be1880c6dde66292e286c533354689e55d926893 Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 16:45:26 +0800 Subject: [PATCH 6/8] set authorization into ActivityLogController --- taskmaster-api/Controllers/ActivityLogController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/taskmaster-api/Controllers/ActivityLogController.cs b/taskmaster-api/Controllers/ActivityLogController.cs index 54aa2e0..a144f3a 100644 --- a/taskmaster-api/Controllers/ActivityLogController.cs +++ b/taskmaster-api/Controllers/ActivityLogController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using taskmaster_api.Data.DTOs; using taskmaster_api.Services.Interface; @@ -7,6 +8,7 @@ namespace taskmaster_api.Controllers { [Route("api/[controller]")] [ApiController] + [Authorize] public class ActivityLogController : ApplicationControllerBase { private readonly IActivityLogService _activityLogService; From 0f601ce7c7239562ea3f4c03982bac1cab3bd074 Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 17:06:26 +0800 Subject: [PATCH 7/8] add tables and endpoints for notification and setting --- .../Controllers/NotificationController.cs | 51 ++++++++ .../Controllers/SettingController.cs | 51 ++++++++ .../Data/Contexts/ApplicationDbContext.cs | 2 + taskmaster-api/Data/DTOs/NotificationDto.cs | 27 ++++- taskmaster-api/Data/DTOs/SettingDto.cs | 21 +++- .../Data/Entities/NotificationEntity.cs | 31 ++++- taskmaster-api/Data/Entities/SettingEntity.cs | 28 ++++- .../Interface/INotificationRepository.cs | 13 +++ .../Interface/ISettingRepository.cs | 13 +++ .../Repositories/NotificationRepository.cs | 58 ++++++++++ .../Data/Repositories/SettingRepository.cs | 58 ++++++++++ ... 20231211090526_InitialCreate.Designer.cs} | 67 ++++++++++- ...ate.cs => 20231211090526_InitialCreate.cs} | 52 ++++++++- .../ApplicationDbContextModelSnapshot.cs | 65 +++++++++++ taskmaster-api/Program.cs | 4 + .../Interface/INotificationService.cs | 14 +++ .../Services/Interface/ISettingService.cs | 14 +++ .../Services/NotificationService.cs | 109 ++++++++++++++++++ taskmaster-api/Services/SettingService.cs | 109 ++++++++++++++++++ 19 files changed, 776 insertions(+), 11 deletions(-) create mode 100644 taskmaster-api/Controllers/NotificationController.cs create mode 100644 taskmaster-api/Controllers/SettingController.cs create mode 100644 taskmaster-api/Data/Repositories/Interface/INotificationRepository.cs create mode 100644 taskmaster-api/Data/Repositories/Interface/ISettingRepository.cs create mode 100644 taskmaster-api/Data/Repositories/NotificationRepository.cs create mode 100644 taskmaster-api/Data/Repositories/SettingRepository.cs rename taskmaster-api/Migrations/{20231211083537_InitialCreate.Designer.cs => 20231211090526_InitialCreate.Designer.cs} (88%) rename taskmaster-api/Migrations/{20231211083537_InitialCreate.cs => 20231211090526_InitialCreate.cs} (88%) create mode 100644 taskmaster-api/Services/Interface/INotificationService.cs create mode 100644 taskmaster-api/Services/Interface/ISettingService.cs create mode 100644 taskmaster-api/Services/NotificationService.cs create mode 100644 taskmaster-api/Services/SettingService.cs diff --git a/taskmaster-api/Controllers/NotificationController.cs b/taskmaster-api/Controllers/NotificationController.cs new file mode 100644 index 0000000..e25b523 --- /dev/null +++ b/taskmaster-api/Controllers/NotificationController.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class NotificationController : ApplicationControllerBase + { + private readonly INotificationService _notificationService; + + public NotificationController(INotificationService notificationService) + { + _notificationService = notificationService; + } + + [HttpGet] + public IActionResult GetAllNotifications() + { + return ToHttpResult>(_notificationService.GetAllNotifications()); + } + + [HttpGet("{id}")] + public IActionResult GetNotification(int id) + { + return ToHttpResult(_notificationService.GetNotificationById(id)); + } + + [HttpPost] + public IActionResult CreateNotification(NotificationDto notificationDto) + { + return ToHttpResult(_notificationService.CreateNotification(notificationDto)); + } + + [HttpPut("{id}")] + public IActionResult UpdateNotification(int id, NotificationDto notificationDto) + { + return ToHttpResult(_notificationService.UpdateNotification(id, notificationDto)); + } + + [HttpDelete("{id}")] + public IActionResult DeleteNotification(int id) + { + return ToHttpResult(_notificationService.DeleteNotification(id)); + } + } +} diff --git a/taskmaster-api/Controllers/SettingController.cs b/taskmaster-api/Controllers/SettingController.cs new file mode 100644 index 0000000..05cd94c --- /dev/null +++ b/taskmaster-api/Controllers/SettingController.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class SettingController : ApplicationControllerBase + { + private readonly ISettingService _settingService; + + public SettingController(ISettingService settingService) + { + _settingService = settingService; + } + + [HttpGet] + public IActionResult GetAllSettings() + { + return ToHttpResult>(_settingService.GetAllSettings()); + } + + [HttpGet("{id}")] + public IActionResult GetSetting(int id) + { + return ToHttpResult(_settingService.GetSettingById(id)); + } + + [HttpPost] + public IActionResult CreateSetting(SettingDto settingDto) + { + return ToHttpResult(_settingService.CreateSetting(settingDto)); + } + + [HttpPut("{id}")] + public IActionResult UpdateSetting(int id, SettingDto settingDto) + { + return ToHttpResult(_settingService.UpdateSetting(id, settingDto)); + } + + [HttpDelete("{id}")] + public IActionResult DeleteSetting(int id) + { + return ToHttpResult(_settingService.DeleteSetting(id)); + } + } +} diff --git a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs index 52c2131..3baad08 100644 --- a/taskmaster-api/Data/Contexts/ApplicationDbContext.cs +++ b/taskmaster-api/Data/Contexts/ApplicationDbContext.cs @@ -16,5 +16,7 @@ public ApplicationDbContext(DbContextOptions options) : base(options) public virtual DbSet Attachments { get; set; } public virtual DbSet Tags { get; set; } public virtual DbSet ActivityLogs { get; set; } + public virtual DbSet Notifications { get; set; } + public virtual DbSet Settings { get; set; } } } diff --git a/taskmaster-api/Data/DTOs/NotificationDto.cs b/taskmaster-api/Data/DTOs/NotificationDto.cs index c03f5b2..06b7004 100644 --- a/taskmaster-api/Data/DTOs/NotificationDto.cs +++ b/taskmaster-api/Data/DTOs/NotificationDto.cs @@ -1,6 +1,29 @@ -namespace taskmaster_api.Data.DTOs +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.Entities; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.DTOs { - public class NotificationDto + public class NotificationDto : IDto { + public int? Id { get; set; } + public string UserId { get; set; } + public string Content { get; set; } + public bool IsRead { get; set; } + public DateTime CreatedAt { get; set; } + + public NotificationDto() + { + if (!Id.HasValue) + { + IsRead = false; + CreatedAt = DateTime.UtcNow; + } + } + + public NotificationEntity ToEntity() + { + return EntityHelpers.ToEntity(this); + } } } diff --git a/taskmaster-api/Data/DTOs/SettingDto.cs b/taskmaster-api/Data/DTOs/SettingDto.cs index 7557772..9eea5da 100644 --- a/taskmaster-api/Data/DTOs/SettingDto.cs +++ b/taskmaster-api/Data/DTOs/SettingDto.cs @@ -1,6 +1,23 @@ -namespace taskmaster_api.Data.DTOs +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.Entities; +using taskmaster_api.Utilities; + +namespace taskmaster_api.Data.DTOs { - public class SettingDto + public class SettingDto : IDto { + public int? Id { get; set; } + public string Name { get; set; } + public string Value { get; set; } + public string? Description { get; set; } + + public SettingDto() + { + } + + public SettingEntity ToEntity() + { + return EntityHelpers.ToEntity(this); + } } } diff --git a/taskmaster-api/Data/Entities/NotificationEntity.cs b/taskmaster-api/Data/Entities/NotificationEntity.cs index 5e1aa80..0cafa19 100644 --- a/taskmaster-api/Data/Entities/NotificationEntity.cs +++ b/taskmaster-api/Data/Entities/NotificationEntity.cs @@ -1,6 +1,33 @@ -namespace taskmaster_api.Data.Entities +using Microsoft.AspNetCore.Identity; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Utilities; +using taskmaster_api.Data.Entities.Interface; + +namespace taskmaster_api.Data.Entities { - public class NotificationEntity + public class NotificationEntity : IEntity { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int? Id { get; set; } + + [Required] + public string UserId { get; set; } + public IdentityUser User { get; set; } + + [Required] + public string Content { get; set; } + + [Required] + public bool IsRead { get; set; } + + public DateTime CreatedAt { get; set; } + + public NotificationDto ToDto() + { + return EntityHelpers.ToDto(this); + } } } diff --git a/taskmaster-api/Data/Entities/SettingEntity.cs b/taskmaster-api/Data/Entities/SettingEntity.cs index 099af6b..fb46950 100644 --- a/taskmaster-api/Data/Entities/SettingEntity.cs +++ b/taskmaster-api/Data/Entities/SettingEntity.cs @@ -1,6 +1,30 @@ -namespace taskmaster_api.Data.Entities +using Microsoft.AspNetCore.Identity; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Utilities; +using taskmaster_api.Data.Entities.Interface; + +namespace taskmaster_api.Data.Entities { - public class SettingEntity + public class SettingEntity : IEntity { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int? Id { get; set; } + + [Required] + [StringLength(100)] + public string Name { get; set; } + + [Required] + public string Value { get; set; } + + public string? Description { get; set; } + + public SettingDto ToDto() + { + return EntityHelpers.ToDto(this); + } } } diff --git a/taskmaster-api/Data/Repositories/Interface/INotificationRepository.cs b/taskmaster-api/Data/Repositories/Interface/INotificationRepository.cs new file mode 100644 index 0000000..564a010 --- /dev/null +++ b/taskmaster-api/Data/Repositories/Interface/INotificationRepository.cs @@ -0,0 +1,13 @@ +using taskmaster_api.Data.Entities; + +namespace taskmaster_api.Data.Repositories.Interface +{ + public interface INotificationRepository + { + NotificationEntity GetNotificationById(int id); + IEnumerable GetAllNotifications(); + NotificationEntity CreateNotification(NotificationEntity notification); + NotificationEntity UpdateNotification(int id, NotificationEntity notification); + int DeleteNotification(int id); + } +} diff --git a/taskmaster-api/Data/Repositories/Interface/ISettingRepository.cs b/taskmaster-api/Data/Repositories/Interface/ISettingRepository.cs new file mode 100644 index 0000000..748aa3c --- /dev/null +++ b/taskmaster-api/Data/Repositories/Interface/ISettingRepository.cs @@ -0,0 +1,13 @@ +using taskmaster_api.Data.Entities; + +namespace taskmaster_api.Data.Repositories.Interface +{ + public interface ISettingRepository + { + SettingEntity GetSettingById(int id); + IEnumerable GetAllSettings(); + SettingEntity CreateSetting(SettingEntity setting); + SettingEntity UpdateSetting(int id, SettingEntity setting); + int DeleteSetting(int id); + } +} diff --git a/taskmaster-api/Data/Repositories/NotificationRepository.cs b/taskmaster-api/Data/Repositories/NotificationRepository.cs new file mode 100644 index 0000000..92228cd --- /dev/null +++ b/taskmaster-api/Data/Repositories/NotificationRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using taskmaster_api.Data.Contexts; +using taskmaster_api.Data.Entities; +using taskmaster_api.Data.Repositories.Interface; + +namespace taskmaster_api.Data.Repositories +{ + public class NotificationRepository : INotificationRepository + { + private readonly ApplicationDbContext _context; + + public NotificationRepository(ApplicationDbContext context) + { + _context = context; + } + + public NotificationEntity GetNotificationById(int id) + { + return _context.Notifications.Find(id); + } + + public IEnumerable GetAllNotifications() + { + return _context.Notifications.ToList(); + } + + public NotificationEntity CreateNotification(NotificationEntity notification) + { + _context.Notifications.Add(notification); + _context.SaveChanges(); + return notification; + } + + public NotificationEntity UpdateNotification(int id, NotificationEntity notification) + { + if (_context.Notifications.Find(id) is NotificationEntity oldNotification) + { + notification.Id = id; + _context.Notifications.Entry(oldNotification).State = EntityState.Detached; + _context.Notifications.Entry(notification).State = EntityState.Modified; + _context.SaveChanges(); + } + return notification; + } + + public int DeleteNotification(int id) + { + var notificationToDelete = _context.Notifications.Find(id); + if (notificationToDelete != null) + { + _context.Notifications.Remove(notificationToDelete); + _context.SaveChanges(); + return id; + } + return -1; + } + } +} diff --git a/taskmaster-api/Data/Repositories/SettingRepository.cs b/taskmaster-api/Data/Repositories/SettingRepository.cs new file mode 100644 index 0000000..5f7b339 --- /dev/null +++ b/taskmaster-api/Data/Repositories/SettingRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using taskmaster_api.Data.Contexts; +using taskmaster_api.Data.Entities; +using taskmaster_api.Data.Repositories.Interface; + +namespace taskmaster_api.Data.Repositories +{ + public class SettingRepository : ISettingRepository + { + private readonly ApplicationDbContext _context; + + public SettingRepository(ApplicationDbContext context) + { + _context = context; + } + + public SettingEntity GetSettingById(int id) + { + return _context.Settings.Find(id); + } + + public IEnumerable GetAllSettings() + { + return _context.Settings.ToList(); + } + + public SettingEntity CreateSetting(SettingEntity setting) + { + _context.Settings.Add(setting); + _context.SaveChanges(); + return setting; + } + + public SettingEntity UpdateSetting(int id, SettingEntity setting) + { + if (_context.Settings.Find(id) is SettingEntity oldSetting) + { + setting.Id = id; + _context.Settings.Entry(oldSetting).State = EntityState.Detached; + _context.Settings.Entry(setting).State = EntityState.Modified; + _context.SaveChanges(); + } + return setting; + } + + public int DeleteSetting(int id) + { + var settingToDelete = _context.Settings.Find(id); + if (settingToDelete != null) + { + _context.Settings.Remove(settingToDelete); + _context.SaveChanges(); + return id; + } + return -1; + } + } +} diff --git a/taskmaster-api/Migrations/20231211083537_InitialCreate.Designer.cs b/taskmaster-api/Migrations/20231211090526_InitialCreate.Designer.cs similarity index 88% rename from taskmaster-api/Migrations/20231211083537_InitialCreate.Designer.cs rename to taskmaster-api/Migrations/20231211090526_InitialCreate.Designer.cs index d63ccfe..556edb7 100644 --- a/taskmaster-api/Migrations/20231211083537_InitialCreate.Designer.cs +++ b/taskmaster-api/Migrations/20231211090526_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace taskmaster_api.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20231211083537_InitialCreate")] + [Migration("20231211090526_InitialCreate")] partial class InitialCreate { /// @@ -319,6 +319,60 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("Comments"); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.NotificationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("taskmaster_api.Data.Entities.SettingEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TagEntity", b => { b.Property("Id") @@ -470,6 +524,17 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + + modelBuilder.Entity("taskmaster_api.Data.Entities.NotificationEntity", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); #pragma warning restore 612, 618 } } diff --git a/taskmaster-api/Migrations/20231211083537_InitialCreate.cs b/taskmaster-api/Migrations/20231211090526_InitialCreate.cs similarity index 88% rename from taskmaster-api/Migrations/20231211083537_InitialCreate.cs rename to taskmaster-api/Migrations/20231211090526_InitialCreate.cs index da79b04..27d5510 100644 --- a/taskmaster-api/Migrations/20231211083537_InitialCreate.cs +++ b/taskmaster-api/Migrations/20231211090526_InitialCreate.cs @@ -50,6 +50,21 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_AspNetUsers", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Settings", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Settings", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Tags", columns: table => new @@ -208,6 +223,28 @@ protected override void Up(MigrationBuilder migrationBuilder) onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "Notifications", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + Content = table.Column(type: "nvarchar(max)", nullable: false), + IsRead = table.Column(type: "bit", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Notifications", x => x.Id); + table.ForeignKey( + name: "FK_Notifications_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "Attachments", columns: table => new @@ -329,6 +366,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_Comments_UserId", table: "Comments", column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Notifications_UserId", + table: "Notifications", + column: "UserId"); } /// @@ -358,6 +400,12 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "Comments"); + migrationBuilder.DropTable( + name: "Notifications"); + + migrationBuilder.DropTable( + name: "Settings"); + migrationBuilder.DropTable( name: "Tags"); @@ -365,10 +413,10 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "AspNetRoles"); migrationBuilder.DropTable( - name: "AspNetUsers"); + name: "Tasks"); migrationBuilder.DropTable( - name: "Tasks"); + name: "AspNetUsers"); } } } diff --git a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs index 8fdeedc..2d5c7be 100644 --- a/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/taskmaster-api/Migrations/ApplicationDbContextModelSnapshot.cs @@ -316,6 +316,60 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Comments"); }); + modelBuilder.Entity("taskmaster_api.Data.Entities.NotificationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("taskmaster_api.Data.Entities.SettingEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + }); + modelBuilder.Entity("taskmaster_api.Data.Entities.TagEntity", b => { b.Property("Id") @@ -467,6 +521,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("User"); }); + + modelBuilder.Entity("taskmaster_api.Data.Entities.NotificationEntity", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); #pragma warning restore 612, 618 } } diff --git a/taskmaster-api/Program.cs b/taskmaster-api/Program.cs index 71831d3..05a9056 100644 --- a/taskmaster-api/Program.cs +++ b/taskmaster-api/Program.cs @@ -64,6 +64,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -72,6 +74,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); diff --git a/taskmaster-api/Services/Interface/INotificationService.cs b/taskmaster-api/Services/Interface/INotificationService.cs new file mode 100644 index 0000000..7c24cc6 --- /dev/null +++ b/taskmaster-api/Services/Interface/INotificationService.cs @@ -0,0 +1,14 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; + +namespace taskmaster_api.Services.Interface +{ + public interface INotificationService + { + ICoreActionResult GetNotificationById(int id); + ICoreActionResult> GetAllNotifications(); + ICoreActionResult CreateNotification(NotificationDto notificationDto); + ICoreActionResult UpdateNotification(int id, NotificationDto notificationDto); + ICoreActionResult DeleteNotification(int id); + } +} diff --git a/taskmaster-api/Services/Interface/ISettingService.cs b/taskmaster-api/Services/Interface/ISettingService.cs new file mode 100644 index 0000000..d85b7fa --- /dev/null +++ b/taskmaster-api/Services/Interface/ISettingService.cs @@ -0,0 +1,14 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; + +namespace taskmaster_api.Services.Interface +{ + public interface ISettingService + { + ICoreActionResult GetSettingById(int id); + ICoreActionResult> GetAllSettings(); + ICoreActionResult CreateSetting(SettingDto settingDto); + ICoreActionResult UpdateSetting(int id, SettingDto settingDto); + ICoreActionResult DeleteSetting(int id); + } +} diff --git a/taskmaster-api/Services/NotificationService.cs b/taskmaster-api/Services/NotificationService.cs new file mode 100644 index 0000000..e4e1ac8 --- /dev/null +++ b/taskmaster-api/Services/NotificationService.cs @@ -0,0 +1,109 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Repositories.Interface; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Services +{ + public class NotificationService : INotificationService + { + private readonly INotificationRepository _notificationRepository; + private readonly ILogger _logger; + + public NotificationService(INotificationRepository notificationRepository, ILogger logger) + { + _notificationRepository = notificationRepository; + _logger = logger; + } + + public ICoreActionResult GetNotificationById(int id) + { + try + { + var notification = _notificationRepository.GetNotificationById(id); + if (notification == null) + { + _logger.LogInformation("Notification not found"); + return CoreActionResult.Failure("Notification not found", "NotFound"); + } + + return CoreActionResult.Success(notification.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult> GetAllNotifications() + { + try + { + var notifications = _notificationRepository.GetAllNotifications(); + return CoreActionResult>.Success(notifications.Select(notification => notification.ToDto()).ToList()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult>.Exception(ex); + } + } + + public ICoreActionResult CreateNotification(NotificationDto notificationDto) + { + try + { + var notification = notificationDto.ToEntity(); + var newNotification = _notificationRepository.CreateNotification(notification); + return CoreActionResult.Success(newNotification.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UpdateNotification(int id, NotificationDto notificationDto) + { + try + { + var notification = notificationDto.ToEntity(); + var existingNotification = _notificationRepository.GetNotificationById(id); + if (existingNotification == null) + { + _logger.LogInformation("Notification not found"); + return CoreActionResult.Failure("Notification not found", "NotFound"); + } + + var updatedNotification = _notificationRepository.UpdateNotification(id, notification); + return CoreActionResult.Success(updatedNotification.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult DeleteNotification(int id) + { + try + { + var deletedNotificationId = _notificationRepository.DeleteNotification(id); + if (deletedNotificationId == 0) + { + _logger.LogInformation("Notification not found"); + return CoreActionResult.Ignore("Notification not found", "NotFound"); + } + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + } +} diff --git a/taskmaster-api/Services/SettingService.cs b/taskmaster-api/Services/SettingService.cs new file mode 100644 index 0000000..bf5d9bb --- /dev/null +++ b/taskmaster-api/Services/SettingService.cs @@ -0,0 +1,109 @@ +using taskmaster_api.Data.DTOs.Interface; +using taskmaster_api.Data.DTOs; +using taskmaster_api.Data.Repositories.Interface; +using taskmaster_api.Services.Interface; + +namespace taskmaster_api.Services +{ + public class SettingService : ISettingService + { + private readonly ISettingRepository _settingRepository; + private readonly ILogger _logger; + + public SettingService(ISettingRepository settingRepository, ILogger logger) + { + _settingRepository = settingRepository; + _logger = logger; + } + + public ICoreActionResult GetSettingById(int id) + { + try + { + var setting = _settingRepository.GetSettingById(id); + if (setting == null) + { + _logger.LogInformation("Setting not found"); + return CoreActionResult.Failure("Setting not found", "NotFound"); + } + + return CoreActionResult.Success(setting.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult> GetAllSettings() + { + try + { + var settings = _settingRepository.GetAllSettings(); + return CoreActionResult>.Success(settings.Select(setting => setting.ToDto()).ToList()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult>.Exception(ex); + } + } + + public ICoreActionResult CreateSetting(SettingDto settingDto) + { + try + { + var setting = settingDto.ToEntity(); + var newSetting = _settingRepository.CreateSetting(setting); + return CoreActionResult.Success(newSetting.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult UpdateSetting(int id, SettingDto settingDto) + { + try + { + var setting = settingDto.ToEntity(); + var existingSetting = _settingRepository.GetSettingById(id); + if (existingSetting == null) + { + _logger.LogInformation("Setting not found"); + return CoreActionResult.Failure("Setting not found", "NotFound"); + } + + var updatedSetting = _settingRepository.UpdateSetting(id, setting); + return CoreActionResult.Success(updatedSetting.ToDto()); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + + public ICoreActionResult DeleteSetting(int id) + { + try + { + var deletedSettingId = _settingRepository.DeleteSetting(id); + if (deletedSettingId == 0) + { + _logger.LogInformation("Setting not found"); + return CoreActionResult.Ignore("Setting not found", "NotFound"); + } + return CoreActionResult.Success(); + } + catch (Exception ex) + { + _logger.LogInformation(ex.Message); + return CoreActionResult.Exception(ex); + } + } + } +} From 9dc7cb45f612406ec8d91683594b246fdf90f29d Mon Sep 17 00:00:00 2001 From: Hai Chi Date: Mon, 11 Dec 2023 17:23:00 +0800 Subject: [PATCH 8/8] set [Required] for all entities --- taskmaster-api/Data/Entities/ActivityLogEntity.cs | 1 + taskmaster-api/Data/Entities/AttachmentEntity.cs | 2 ++ taskmaster-api/Data/Entities/CommentEntity.cs | 2 ++ taskmaster-api/Data/Entities/NotificationEntity.cs | 1 + taskmaster-api/Data/Entities/TaskEntity.cs | 3 +++ 5 files changed, 9 insertions(+) diff --git a/taskmaster-api/Data/Entities/ActivityLogEntity.cs b/taskmaster-api/Data/Entities/ActivityLogEntity.cs index 30848e0..a56672e 100644 --- a/taskmaster-api/Data/Entities/ActivityLogEntity.cs +++ b/taskmaster-api/Data/Entities/ActivityLogEntity.cs @@ -21,6 +21,7 @@ public class ActivityLogEntity : IEntity [StringLength(100)] public string Action { get; set; } + [Required] public DateTime CreatedAt { get; set; } public ActivityLogDto ToDto() diff --git a/taskmaster-api/Data/Entities/AttachmentEntity.cs b/taskmaster-api/Data/Entities/AttachmentEntity.cs index 442ffdd..8693611 100644 --- a/taskmaster-api/Data/Entities/AttachmentEntity.cs +++ b/taskmaster-api/Data/Entities/AttachmentEntity.cs @@ -25,8 +25,10 @@ public class AttachmentEntity : IEntity [StringLength(255)] public string FileName { get; set; } + [Required] public DateTime CreatedAt { get; set; } + [Required] public DateTime UpdatedAt { get; set; } public AttachmentDto ToDto() diff --git a/taskmaster-api/Data/Entities/CommentEntity.cs b/taskmaster-api/Data/Entities/CommentEntity.cs index 94d7cfd..9609a79 100644 --- a/taskmaster-api/Data/Entities/CommentEntity.cs +++ b/taskmaster-api/Data/Entities/CommentEntity.cs @@ -24,8 +24,10 @@ public class CommentEntity : IEntity [Required] public string Content { get; set; } + [Required] public DateTime CreatedAt { get; set; } + [Required] public DateTime UpdatedAt { get; set; } public CommentDto ToDto() diff --git a/taskmaster-api/Data/Entities/NotificationEntity.cs b/taskmaster-api/Data/Entities/NotificationEntity.cs index 0cafa19..f712ac3 100644 --- a/taskmaster-api/Data/Entities/NotificationEntity.cs +++ b/taskmaster-api/Data/Entities/NotificationEntity.cs @@ -23,6 +23,7 @@ public class NotificationEntity : IEntity [Required] public bool IsRead { get; set; } + [Required] public DateTime CreatedAt { get; set; } public NotificationDto ToDto() diff --git a/taskmaster-api/Data/Entities/TaskEntity.cs b/taskmaster-api/Data/Entities/TaskEntity.cs index 1642b2c..a458572 100644 --- a/taskmaster-api/Data/Entities/TaskEntity.cs +++ b/taskmaster-api/Data/Entities/TaskEntity.cs @@ -20,11 +20,14 @@ public class TaskEntity : IEntity public DateTime? DueDate { get; set; } + [Required] [StringLength(50)] public string Status { get; set; } + [Required] public DateTime CreatedAt { get; set; } + [Required] public DateTime UpdatedAt { get; set; } public TaskDto ToDto()