diff --git a/README.md b/README.md index 32addd9..997ed3e 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,23 @@ # Kilka ważnych informacji -Przed przystąpieniem do rozwiązywania zadań przeczytaj poniższe wskazówki +Przed przystąpieniem do rozwiązywania zadań przeczytaj poniższe wskazówki. ## Jak zacząć? -1. Stwórz [*fork*](https://guides.github.com/activities/forking/) repozytorium z zadaniami. -2. Sklonuj fork repozytorium (stworzony w punkcie 1) na swój komputer. Użyj do tego komendy `git clone adres_repozytorium` -Adres możesz znaleźć na stronie forka repozytorium po naciśnięciu w guzik "Clone or download". -3. Rozwiąż zadania i skomituj zmiany do swojego repozytorium. Użyj do tego komend `git add nazwa_pliku`. +1. Sklonuj oryginalne repozytorium na swój komputer (nie klonuj forka). Użyj do tego komendy `git clone adres_repozytorium` +Adres możesz znaleźć na stronie repozytorium po naciśnięciu w guzik "Clone or download". +2. Przełącz się na branch `gitflow-NUMER_KURSU` tego repozytorium (w miejsce NUMER_KURSU podstaw rzeczywistą sygnaturę kursu). +3. Utwórz właśny unikalny branch `feature/X`, wstawiając za `X` unikalną nazwę, np. numer kursu i Twoje inicjały z liczbą. +4. Rozwiąż zadania i skomituj zmiany do swojego brancha z punktu 3. Użyj do tego komend `git add nazwa_pliku`. Jeżeli chcesz dodać wszystkie zmienione pliki użyj `git add .` Pamiętaj że kropka na końcu jest ważna! Następnie skommituj zmiany komendą `git commit -m "nazwa_commita"` -4. Wypchnij zmiany do swojego repozytorium na GitHubie. Użyj do tego komendy `git push origin master` -5. Stwórz [*pull request*](https://help.github.com/articles/creating-a-pull-request) do oryginalnego repozytorium, gdy skończysz wszystkie zadania. +5. Wypchnij zmiany do swojego brancha w repozytorium na GitHubie. Użyj do tego komendy `git push origin feature/` (podstaw za `X` Twoją nazwę brancha). +6. Stwórz [*pull request*](https://help.github.com/articles/creating-a-pull-request) z Twojego brancha do brancha `gitflow-NUMER_KURSU`, gdy skończysz wszystkie zadania. Poszczególne zadania rozwiązuj w odpowiednich plikach. ### Poszczególne zadania rozwiązuj w odpowiednich plikach. **Repozytorium z ćwiczeniami zostanie usunięte 2 tygodnie po zakończeniu kursu. Spowoduje to też usunięcie wszystkich forków, które są zrobione z tego repozytorium.** - - -## Warsztat TaskManager - -Jest to gotowa - wzorcowa wersja warsztatu TaskManager. diff --git a/TaskManager.Tests/MockRepository.cs b/TaskManager.Tests/MockRepository.cs new file mode 100644 index 0000000..60369a4 --- /dev/null +++ b/TaskManager.Tests/MockRepository.cs @@ -0,0 +1,46 @@ +using TaskManager.BusinessLogic; + +namespace TaskManager.Tests +{ + public class MockRepository : IRepository + { + private int _taskId = 0; + private List _tasks = new List(); + + private List _users = new List { new User(1, "Ja") }; + + public async Task GetAllUsersAsync() => _users.ToArray(); + + public async Task GetUserByIdAsync(int userId) => _users.FirstOrDefault(u => u.Id == userId); + + public async Task CreateTaskItemAsync(TaskItem newTaskItem) + { + var newTask = new TaskItem(newTaskItem.Id == 0 ? ++_taskId : newTaskItem.Id, newTaskItem.Description, newTaskItem.CreatedBy, newTaskItem.DueDate); + _tasks.Add(newTask); + return newTask.Id; + } + + public async Task UpdateTaskItemAsync(TaskItem newTaskItem) + { + var result = await DeleteTaskItemAsync(newTaskItem.Id); + if (result) + _tasks.Add(newTaskItem); + return result; + } + + public async Task DeleteTaskItemAsync(int taskItemId) + { + var task = await GetTaskItemByIdAsync(taskItemId); + return _tasks.Remove(task); + } + + public async Task GetTaskItemByIdAsync(int taskItemId) => _tasks.Find(t => t.Id == taskItemId); + + public async Task GetAllTaskItemsAsync() => _tasks.ToArray(); + + public async Task GetTaskItemsByStatusAsync(TaskItemStatus status) => _tasks.Where(t => t.Status == status).ToArray(); + + public async Task GetTaskItemsByDescriptionAsync(string description) => + _tasks.FindAll(t => t.Description.Contains(description, StringComparison.InvariantCultureIgnoreCase)).ToArray(); + } +} \ No newline at end of file diff --git a/TaskManager.Tests/TaskTests.cs b/TaskManager.Tests/TaskItemTests.cs similarity index 56% rename from TaskManager.Tests/TaskTests.cs rename to TaskManager.Tests/TaskItemTests.cs index 7838526..538fab4 100644 --- a/TaskManager.Tests/TaskTests.cs +++ b/TaskManager.Tests/TaskItemTests.cs @@ -1,26 +1,25 @@ -//Task i TaskStatus już istnieją w przestrzeni System.Threading.Tasks, która jest automatycznie importowana. -//Musimy rozwiązać konflikt nazw stosując aliasy. -using Task = TaskManager.BusinessLogic.Task; -using TaskStatus = TaskManager.BusinessLogic.TaskStatus; +using TaskManager.BusinessLogic; namespace TaskManager.Tests { - public class TaskTests + public class TaskItemTests { + private User _createdBy = new User(1, "Ja"); + [Fact] public void Should_CreateTask_WithAutoIncrementedId() { - var task1 = new Task("Test task 1", null); - var task2 = new Task("Test task 2", null); + var task1 = new TaskItem(1, "Test task 1", _createdBy, null); + var task2 = new TaskItem(2, "Test task 2", _createdBy, null); Assert.True(task1.Id > 0); - Assert.Equal(task1.Id + 1, task2.Id); + Assert.True(task1.Id < task2.Id); } [Fact] public void Should_SetCreationDate_WhenCreatingTask() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); var difference = DateTime.Now - task.CreationDate; Assert.True(difference.TotalSeconds < 1); @@ -30,7 +29,7 @@ public void Should_SetCreationDate_WhenCreatingTask() public void Should_SetDueDate_WhenProvided() { var dueDate = DateTime.Now.AddDays(7); - var task = new Task("Test task", dueDate); + var task = new TaskItem(1, "Test task", _createdBy, dueDate); Assert.Equal(dueDate, task.DueDate); } @@ -38,26 +37,26 @@ public void Should_SetDueDate_WhenProvided() [Fact] public void Should_SetStatusToTodo_WhenTaskIsCreated() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); - Assert.Equal(TaskStatus.ToDo, task.Status); + Assert.Equal(TaskItemStatus.ToDo, task.Status); } [Fact] public void Should_ChangeStatus_ToInProgress_WhenStartIsCalled() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); bool result = task.Start(); Assert.True(result); - Assert.Equal(TaskStatus.InProgress, task.Status); + Assert.Equal(TaskItemStatus.InProgress, task.Status); } [Fact] public void Should_SetStartDate_WhenStartIsCalled() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); task.Start(); @@ -69,31 +68,31 @@ public void Should_SetStartDate_WhenStartIsCalled() [Fact] public void Should_NotChangeStatus_ToInProgress_IfAlreadyInProgress() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); task.Start(); bool result = task.Start(); Assert.False(result); - Assert.Equal(TaskStatus.InProgress, task.Status); + Assert.Equal(TaskItemStatus.InProgress, task.Status); } [Fact] public void Should_ChangeStatus_ToDone_WhenDoneIsCalledAndStatusIsInProgress() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); task.Start(); bool result = task.Done(); Assert.True(result); - Assert.Equal(TaskStatus.Done, task.Status); + Assert.Equal(TaskItemStatus.Done, task.Status); } [Fact] public void Should_SetDoneDate_WhenDoneIsCalled() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); task.Start(); task.Done(); @@ -106,18 +105,18 @@ public void Should_SetDoneDate_WhenDoneIsCalled() [Fact] public void Should_NotChangeStatus_ToDone_IfStatusIsNotInProgress() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); bool result = task.Done(); Assert.False(result); - Assert.Equal(TaskStatus.ToDo, task.Status); + Assert.Equal(TaskItemStatus.ToDo, task.Status); } [Fact] public void Should_CalculateDuration_WhenStatusIsInProgress() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); task.Start(); var duration = task.Duration; @@ -128,11 +127,32 @@ public void Should_CalculateDuration_WhenStatusIsInProgress() [Fact] public void Should_ReturnNullDuration_WhenStatusIsTodo() { - var task = new Task("Test task", null); + var task = new TaskItem(1, "Test task", _createdBy, null); var duration = task.Duration; Assert.Null(duration); } + + [Fact] + public void Should_AssignTo_User() + { + var task = new TaskItem(1, "Test task", _createdBy, null); + + task.AssignTo(_createdBy); + + Assert.Equal(_createdBy, task.AssignedTo); + } + + [Fact] + public void Should_Unassign_User_When_Null_Passed() + { + var task = new TaskItem(1, "Test task", _createdBy, null); + task.AssignTo(_createdBy); + + task.AssignTo(null); + + Assert.Null(task.AssignedTo); + } } } \ No newline at end of file diff --git a/TaskManager.Tests/TaskManagerServiceTests.cs b/TaskManager.Tests/TaskManagerServiceTests.cs index 0d6824c..3525db0 100644 --- a/TaskManager.Tests/TaskManagerServiceTests.cs +++ b/TaskManager.Tests/TaskManagerServiceTests.cs @@ -1,131 +1,182 @@ using TaskManager.BusinessLogic; -//TaskStatus już istnieje w przestrzeni System.Threading.Tasks, która jest automatycznie importowana. -//Musimy rozwiązać konflikt nazw stosując alias. -using TaskStatus = TaskManager.BusinessLogic.TaskStatus; namespace TaskManager.Tests { public class TaskManagerServiceTests { + private readonly int _createdBy = 1; + [Fact] - public void Should_AddTask_ToTaskList() + public async Task Should_AddTask_ToTaskList() { - var service = new TaskManagerService(); + var service = new TaskManagerService(new MockRepository()); - var task = service.Add("Test task", DateTime.Now.AddDays(5)); + var task = await service.AddAsync("Test task", _createdBy, DateTime.Now.AddDays(5)); Assert.NotNull(task); - Assert.Single(service.GetAll()); + Assert.Single(await service.GetAllAsync()); } [Fact] - public void Should_RemoveTask_ByTaskId() + public async Task Should_RemoveTask_ByTaskId() { - var service = new TaskManagerService(); - var task = service.Add("Test task", null); + var service = new TaskManagerService(new MockRepository()); + var task = await service.AddAsync("Test task", _createdBy, null); - bool result = service.Remove(task.Id); + bool result = await service.RemoveAsync(task.Id); Assert.True(result); - Assert.Empty(service.GetAll()); + Assert.Empty(await service.GetAllAsync()); } [Fact] - public void Should_NotRemoveTask_WhenTaskIdDoesNotExist() + public async Task Should_NotRemoveTask_WhenTaskIdDoesNotExist() { - var service = new TaskManagerService(); - service.Add("Test task", null); + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Test task", _createdBy, null); - bool result = service.Remove(999); + bool result = await service.RemoveAsync(999); Assert.False(result); - Assert.Single(service.GetAll()); + Assert.Single(await service.GetAllAsync()); } [Fact] - public void Should_GetTask_ByTaskId() + public async Task Should_GetTask_ByTaskId() { - var service = new TaskManagerService(); - var task = service.Add("Test task", null); + var service = new TaskManagerService(new MockRepository()); + var task = await service.AddAsync("Test task", _createdBy, null); - var retrievedTask = service.Get(task.Id); + var retrievedTask = await service.GetAsync(task.Id); Assert.NotNull(retrievedTask); Assert.Equal(task.Id, retrievedTask.Id); } [Fact] - public void Should_GetAllTasks_WithNoFilter() + public async Task Should_GetAllTasks_WithNoFilter() { - var service = new TaskManagerService(); - service.Add("Test task 1", null); - service.Add("Test task 2", null); + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Test task 1", _createdBy, null); + await service.AddAsync("Test task 2", _createdBy, null); - var tasks = service.GetAll(); + var tasks = await service.GetAllAsync(); Assert.Equal(2, tasks.Length); } [Fact] - public void Should_GetTasks_ByStatus() + public async Task Should_GetTasks_ByStatus() { - var service = new TaskManagerService(); - var task1 = service.Add("Test task 1", null); + var service = new TaskManagerService(new MockRepository()); + var task1 = await service.AddAsync("Test task 1", _createdBy, null); task1.Start(); - service.Add("Test task 2", null); + await service.AddAsync("Test task 2", _createdBy, null); - var inProgressTasks = service.GetAll(TaskStatus.InProgress); + var inProgressTasks = await service.GetAllAsync(TaskItemStatus.InProgress); Assert.Single(inProgressTasks); Assert.Equal(task1.Id, inProgressTasks.First().Id); } [Fact] - public void Should_GetTasks_ByDescription() + public async Task Should_GetTasks_ByDescription() { - var service = new TaskManagerService(); - service.Add("Unique test task", null); - service.Add("Test task 2", null); + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Unique test task", _createdBy, null); + await service.AddAsync("Test task 2", _createdBy, null); - var tasks = service.GetAll("Unique"); + var tasks = await service.GetAllAsync("Unique"); Assert.Single(tasks); Assert.Equal("Unique test task", tasks.First().Description); } [Fact] - public void Should_ChangeTaskStatus_WhenValid() + public async Task Should_ChangeTaskStatus_WhenValid() + { + var service = new TaskManagerService(new MockRepository()); + var task = await service.AddAsync("Test task", _createdBy, null); + + bool result = await service.ChangeStatusAsync(task.Id, TaskItemStatus.InProgress); + + Assert.True(result); + Assert.Equal(TaskItemStatus.InProgress, task.Status); + } + + [Fact] + public async Task Should_NotChangeTaskStatus_WhenInvalidTransition() + { + var service = new TaskManagerService(new MockRepository()); + var task = await service.AddAsync("Test task", _createdBy, null); + + bool result = await service.ChangeStatusAsync(task.Id, TaskItemStatus.Done); + + Assert.False(result); + Assert.Equal(TaskItemStatus.ToDo, task.Status); + } + + [Fact] + public async Task Should_NotChangeTaskStatus_WhenTaskIdDoesNotExist() + { + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Test task", _createdBy, null); + + bool result = await service.ChangeStatusAsync(999, TaskItemStatus.Done); + + Assert.False(result); + } + + [Fact] + public async void Should_Assign_ExistingUser_To_ExistingTask() + { + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Test task", _createdBy, null); + + var result = await service.AssignToAsync(1, _createdBy); + + var task = await service.GetAsync(1); + Assert.True(result); + Assert.Equal(_createdBy, task.AssignedTo.Id); + } + + [Fact] + public async void Should_Unassign_User_From_Task() { - var service = new TaskManagerService(); - var task = service.Add("Test task", null); + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Test task", _createdBy, null); + await service.AssignToAsync(1, _createdBy); - bool result = service.ChangeStatus(task.Id, TaskStatus.InProgress); + var result = await service.AssignToAsync(1, null); + var task = await service.GetAsync(1); Assert.True(result); - Assert.Equal(TaskStatus.InProgress, task.Status); + Assert.Null(task.AssignedTo); } [Fact] - public void Should_NotChangeTaskStatus_WhenInvalidTransition() + public async void Should_NotAssign_NotExistingUser_To_ExistingTask() { - var service = new TaskManagerService(); - var task = service.Add("Test task", null); + var service = new TaskManagerService(new MockRepository()); + await service.AddAsync("Test task", _createdBy, null); - bool result = service.ChangeStatus(task.Id, TaskStatus.Done); + var result = await service.AssignToAsync(1, 999); + var task = await service.GetAsync(1); Assert.False(result); - Assert.Equal(TaskStatus.ToDo, task.Status); + Assert.Null(task.AssignedTo); } [Fact] - public void Should_NotChangeTaskStatus_WhenTaskIdDoesNotExist() + public async void Should_NotAssign_ExistingUser_To_NotExistingTask() { - var service = new TaskManagerService(); - service.Add("Test task", null); + var service = new TaskManagerService(new MockRepository()); - bool result = service.ChangeStatus(999, TaskStatus.Done); + var result = await service.AssignToAsync(1, 1); + var task = await service.GetAsync(1); Assert.False(result); + Assert.Null(task); } } } diff --git a/TaskManager/BusinessLogic/DapperExtensions.cs b/TaskManager/BusinessLogic/DapperExtensions.cs new file mode 100644 index 0000000..cdf0499 --- /dev/null +++ b/TaskManager/BusinessLogic/DapperExtensions.cs @@ -0,0 +1,21 @@ +using System.Reflection; + +namespace TaskManager.BusinessLogic +{ + public static class DapperExtensions + { + public static TaskItem FixDapperMapping(this TaskItem taskItem, User createdBy, User assignedTo) + { + SetValueToObject(taskItem, "_createdBy", createdBy); + SetValueToObject(taskItem, "_assignedTo", assignedTo); + return taskItem; + } + + private static void SetValueToObject(object obj, string fieldName, object value) + { + var type = obj.GetType(); + var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + field.SetValue(obj, value); + } + } +} \ No newline at end of file diff --git a/TaskManager/BusinessLogic/IRepository.cs b/TaskManager/BusinessLogic/IRepository.cs new file mode 100644 index 0000000..2cd4860 --- /dev/null +++ b/TaskManager/BusinessLogic/IRepository.cs @@ -0,0 +1,21 @@ +namespace TaskManager.BusinessLogic +{ + public interface IRepository + { + Task GetAllUsersAsync(); + Task GetUserByIdAsync(int userId); + Task CreateTaskItemAsync(TaskItem newTaskItem); + Task UpdateTaskItemAsync(TaskItem newTaskItem); + Task DeleteTaskItemAsync(int taskItemId); + Task GetTaskItemByIdAsync(int taskItemId); + Task GetAllTaskItemsAsync(); + Task GetTaskItemsByStatusAsync(TaskItemStatus status); + Task GetTaskItemsByDescriptionAsync(string description); + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task> GetLogCountByDayAsync(); + Task> GetByLogLevelAsync(string logLevel); + Task> GetByMessageAsync(string searchTerm); + + } +} \ No newline at end of file diff --git a/TaskManager/BusinessLogic/LogCountByDayDto.cs b/TaskManager/BusinessLogic/LogCountByDayDto.cs new file mode 100644 index 0000000..bf4c2ce --- /dev/null +++ b/TaskManager/BusinessLogic/LogCountByDayDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TaskManager.BusinessLogic +{ + public class LogCountByDayDto + { + public DateTime Day { get; set; } + public int Count { get; set; } + } +} diff --git a/TaskManager/BusinessLogic/Repository.cs b/TaskManager/BusinessLogic/Repository.cs new file mode 100644 index 0000000..caa48ec --- /dev/null +++ b/TaskManager/BusinessLogic/Repository.cs @@ -0,0 +1,216 @@ +using System.Data.SqlClient; +using System.Reflection; +using Dapper; + +namespace TaskManager.BusinessLogic +{ + public class Repository : IRepository + { + private readonly string _connectionString; + + public Repository(string connectionString) + { + _connectionString = connectionString; + } + + public async Task GetAllUsersAsync() + { + using (var connection = new SqlConnection(_connectionString)) + { + var users = await connection.QueryAsync("SELECT * FROM Users"); + return users.ToArray(); + } + } + + public async Task GetUserByIdAsync(int userId) + { + using (var connection = new SqlConnection(_connectionString)) + { + var user = await connection.QueryFirstAsync("SELECT * FROM Users WHERE Id = @Id", new {Id = userId}); + return user; + } + } + + public async Task CreateTaskItemAsync(TaskItem newTaskItem) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = + "INSERT INTO TaskItems (Description, CreatedById, CreationDate, Status, DueDate) " + + "VALUES (@Description, @CreatedById, @CreationDate, @Status, @DueDate);" + + "SELECT SCOPE_IDENTITY();"; + var newTask = new + { + Description = newTaskItem.Description, + CreatedById = newTaskItem.CreatedBy.Id, + CreationDate = newTaskItem.CreationDate, + Status = newTaskItem.Status, + DueDate = newTaskItem.DueDate + }; + var id = await connection.ExecuteScalarAsync(sql, newTask); + return id; + } + } + + public async Task UpdateTaskItemAsync(TaskItem newTaskItem) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = + "UPDATE TaskItems " + + "SET Status = @Status, StartDate = @StartDate, DoneDate = @DoneDate, AssignedToId = @AssignedTo " + + "WHERE Id = @Id"; + + var updateTask = new + { + Id = newTaskItem.Id, + Status = newTaskItem.Status, + StartDate = newTaskItem.StartDate, + DoneDate = newTaskItem.DoneDate, + AssignedTo = newTaskItem.AssignedTo?.Id + }; + var result = await connection.ExecuteAsync(sql, updateTask); + return result == 1; + } + } + + public async Task DeleteTaskItemAsync(int taskItemId) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = "DELETE FROM TaskItems WHERE Id = @Id"; + var result = await connection.ExecuteAsync(sql, new {Id = taskItemId}); + return result == 1; + } + } + + public async Task GetTaskItemByIdAsync(int taskItemId) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = + "SELECT task.*, createdBy.*, assignedTo.* FROM TaskItems task " + + "INNER JOIN Users createdBy ON createdBy.Id = task.CreatedById " + + "LEFT JOIN Users assignedTo ON assignedTo.Id = task.AssignedToId " + + "WHERE task.Id = @TaskId"; + var tasks = await connection.QueryAsync( + sql, + (task, createdBy, assignedTo) => + { + return task.FixDapperMapping(createdBy, assignedTo); + }, + new {TaskId = taskItemId}); + return tasks.FirstOrDefault(); + } + } + + public async Task GetAllTaskItemsAsync() + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = + "SELECT task.*, createdBy.*, assignedTo.* FROM TaskItems task " + + "INNER JOIN Users createdBy ON createdBy.Id = task.CreatedById " + + "LEFT JOIN Users assignedTo ON assignedTo.Id = task.AssignedToId"; + var tasks = await connection.QueryAsync( + sql, + (task, createdBy, assignedTo) => + { + return task.FixDapperMapping(createdBy, assignedTo); + }); + return tasks.ToArray(); + } + } + + public async Task GetTaskItemsByStatusAsync(TaskItemStatus status) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = + "SELECT task.*, createdBy.*, assignedTo.* FROM TaskItems task " + + "INNER JOIN Users createdBy ON createdBy.Id = task.CreatedById " + + "LEFT JOIN Users assignedTo ON assignedTo.Id = task.AssignedToId " + + "WHERE Task.Status = @TaskStatus"; + var tasks = await connection.QueryAsync( + sql, + (task, createdBy, assignedTo) => + { + return task.FixDapperMapping(createdBy, assignedTo); + }, + new {TaskStatus = status}); + return tasks.ToArray(); + } + } + + public async Task GetTaskItemsByDescriptionAsync(string description) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = + "SELECT task.*, createdBy.*, assignedTo.* FROM TaskItems task " + + "INNER JOIN Users createdBy ON createdBy.Id = task.CreatedById " + + "LEFT JOIN Users assignedTo ON assignedTo.Id = task.AssignedToId " + + "WHERE Task.Description LIKE @Description"; + var tasks = await connection.QueryAsync( + sql, + (task, createdBy, assignedTo) => + { + return task.FixDapperMapping(createdBy, assignedTo); + }, + new {Description = $"%{description}%"}); + return tasks.ToArray(); + } + } + public async Task> GetAllAsync() + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = "SELECT * FROM SeriLogs"; + return await connection.QueryAsync(sql); + } + } + public async Task GetByIdAsync(int id) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = "SELECT * FROM SeriLogs WHERE Id = @Id"; + return (await connection.QueryAsync(sql, new { Id = id })).FirstOrDefault(); + } + } + public async Task> GetLogCountByDayAsync() + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = @" + SELECT + CAST(TimeStamp AS DATE) AS Day, + COUNT(*) AS Count + FROM SeriLogs + GROUP BY CAST(TimeStamp AS DATE) + ORDER BY Day"; + var results = await connection.QueryAsync(sql); + return results; + } + } + public async Task> GetByLogLevelAsync(string logLevel) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = @" + SELECT * + FROM SeriLogs + WHERE Level = @Level"; + var results = await connection.QueryAsync(sql, new { Level = logLevel }); + return results; + } + } + public async Task> GetByMessageAsync(string searchTerm) + { + using (var connection = new SqlConnection(_connectionString)) + { + var sql = "SELECT * FROM SeriLogs WHERE Message LIKE @SearchTerm"; + return await connection.QueryAsync(sql, new { SearchTerm = $"%{searchTerm}%" }); + } + } + } +} \ No newline at end of file diff --git a/TaskManager/BusinessLogic/SeriLog.cs b/TaskManager/BusinessLogic/SeriLog.cs new file mode 100644 index 0000000..6feb390 --- /dev/null +++ b/TaskManager/BusinessLogic/SeriLog.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TaskManager.BusinessLogic +{ + public class SeriLog + { + public int Id { get; set; } + public string Message { get; set; } + public string MessageTemplate { get; set; } + public string Level { get; set; } + public DateTime TimeStamp { get; set; } + public string Exception { get; set; } + public string Properties { get; set; } + } +} diff --git a/TaskManager/BusinessLogic/SerilogController.cs b/TaskManager/BusinessLogic/SerilogController.cs new file mode 100644 index 0000000..c510afd --- /dev/null +++ b/TaskManager/BusinessLogic/SerilogController.cs @@ -0,0 +1,58 @@ +using TaskManager.BusinessLogic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System.Runtime.InteropServices; + +namespace TaskManager.BusinessLogic +{ + [Route("api/[controller]")] + [ApiController] + public class SeriLogController : ControllerBase + { + private IRepository _repository; + + public SeriLogController(IRepository logRepository) + { + _repository = logRepository; + } + + [HttpGet] + public async Task GetAll() + { + var logs = await _repository.GetAllAsync(); + return Ok(logs); + } + + [HttpGet("{id}")] + public async Task GetById(int id) + { + var log = await _repository.GetByIdAsync(id); + if (log == null) + { + return NotFound(); + } + return Ok(log); + } + + [HttpGet("count-by-day")] + public async Task GetLogCountByDay() + { + var logs = await _repository.GetLogCountByDayAsync(); + return Ok(logs); + } + + [HttpGet("get-by-logLevel")] + public async Task GetByLogLevel(string logLevel) + { + var logs = await _repository.GetByLogLevelAsync(logLevel); + return Ok(logs); + } + + [HttpGet("get-by-message")] + public async Task GetByMessage(string message) + { + var logs = await _repository.GetByMessageAsync(message); + return Ok(logs); + } + } +} diff --git a/TaskManager/BusinessLogic/Task.cs b/TaskManager/BusinessLogic/TaskItem.cs similarity index 54% rename from TaskManager/BusinessLogic/Task.cs rename to TaskManager/BusinessLogic/TaskItem.cs index 9c90f88..43da001 100644 --- a/TaskManager/BusinessLogic/Task.cs +++ b/TaskManager/BusinessLogic/TaskItem.cs @@ -1,9 +1,9 @@ namespace TaskManager.BusinessLogic { - public class Task + public class TaskItem { - private static int _id; - + private User _createdBy; + private User? _assignedTo; public int Id { get; } public string Description { get; set; } @@ -18,22 +18,28 @@ public class Task public TimeSpan? Duration => StartDate != null ? (DoneDate ?? DateTime.Now) - StartDate.Value : null; - public TaskStatus Status { get; private set; } = TaskStatus.ToDo; + public TaskItemStatus Status { get; private set; } = TaskItemStatus.ToDo; + + public User CreatedBy => _createdBy; + public User? AssignedTo => _assignedTo; + + private TaskItem() { } - public Task(string description, DateTime? dueDate) + public TaskItem(int id, string description, User createdBy, DateTime? dueDate) { - Id = ++_id; + Id = id; Description = description; CreationDate = DateTime.Now; + _createdBy = createdBy; DueDate = dueDate; } public bool Start() { - if (Status == TaskStatus.InProgress) + if (Status == TaskItemStatus.InProgress) return false; - Status = TaskStatus.InProgress; + Status = TaskItemStatus.InProgress; StartDate = DateTime.Now; DoneDate = null; return true; @@ -41,10 +47,10 @@ public bool Start() public bool Open() { - if (Status == TaskStatus.ToDo) + if (Status == TaskItemStatus.ToDo) return false; - Status = TaskStatus.ToDo; + Status = TaskItemStatus.ToDo; StartDate = null; DoneDate = null; return true; @@ -52,17 +58,22 @@ public bool Open() public bool Done() { - if (Status != TaskStatus.InProgress) + if (Status != TaskItemStatus.InProgress) return false; - Status = TaskStatus.Done; + Status = TaskItemStatus.Done; DoneDate = DateTime.Now; return true; } + public void AssignTo(User? assignedTo) + { + _assignedTo = assignedTo; + } + public override string ToString() { - return $"{Id} - {Description} ({Status})"; + return $"{Id} - {Description} ({Status}) @{AssignedTo?.Name ?? "nieprzypisane"}"; } } } \ No newline at end of file diff --git a/TaskManager/BusinessLogic/TaskStatus.cs b/TaskManager/BusinessLogic/TaskItemStatus.cs similarity index 75% rename from TaskManager/BusinessLogic/TaskStatus.cs rename to TaskManager/BusinessLogic/TaskItemStatus.cs index ba6f741..2042169 100644 --- a/TaskManager/BusinessLogic/TaskStatus.cs +++ b/TaskManager/BusinessLogic/TaskItemStatus.cs @@ -1,6 +1,6 @@ namespace TaskManager.BusinessLogic { - public enum TaskStatus + public enum TaskItemStatus { ToDo, InProgress, diff --git a/TaskManager/BusinessLogic/TaskManagerService.cs b/TaskManager/BusinessLogic/TaskManagerService.cs index 4e213fc..738c991 100644 --- a/TaskManager/BusinessLogic/TaskManagerService.cs +++ b/TaskManager/BusinessLogic/TaskManagerService.cs @@ -2,63 +2,96 @@ namespace TaskManager.BusinessLogic { public class TaskManagerService { - private List _tasks = new List(); + private readonly IRepository _repository; - public Task Add(string description, DateTime? dueDate) + public TaskManagerService(IRepository repository) { - var task = new Task(description, dueDate); - _tasks.Add(task); - return task; + _repository = repository; } - public bool Remove(int taskId) + public async Task AddAsync(string description, int createdBy, DateTime? dueDate) { - var task = Get(taskId); + var user = await _repository.GetUserByIdAsync(createdBy); + var task = new TaskItem(0, description, user, dueDate); + var id = await _repository.CreateTaskItemAsync(task); + return await GetAsync(id); + } + + public async Task RemoveAsync(int taskId) + { + var task = await GetAsync(taskId); if (task != null) - return _tasks.Remove(task); + return await _repository.DeleteTaskItemAsync(task.Id); return false; } - public Task Get(int taskId) + public async Task GetAsync(int taskId) { - return _tasks.Find(t => t.Id == taskId); + return await _repository.GetTaskItemByIdAsync(taskId); } - public Task[] GetAll() + public async Task GetAllAsync() { - return _tasks.ToArray(); + return await _repository.GetAllTaskItemsAsync(); } - public Task[] GetAll(TaskStatus status) + public async Task GetAllAsync(TaskItemStatus itemStatus) { - return _tasks.FindAll(t => t.Status == status).ToArray(); + return await _repository.GetTaskItemsByStatusAsync(itemStatus); } - public Task[] GetAll(string description) + public async Task GetAllAsync(string description) { - // Przeciążona wersja Contains przyjmuje drugi parametr jako opcje porównania tekstu, - // gdzie możemy wskazać, aby przy porównaniu pomijać wielkość liter - return _tasks.FindAll(t => t.Description.Contains(description, StringComparison.InvariantCultureIgnoreCase)).ToArray(); + return await _repository.GetTaskItemsByDescriptionAsync(description); } - public bool ChangeStatus(int taskId, TaskStatus newStatus) + public async Task ChangeStatusAsync(int taskId, TaskItemStatus newStatus) { - var task = Get(taskId); + var task = await GetAsync(taskId); if (task == null || task?.Status == newStatus) return false; + var result = ChangeStatus(task, newStatus); + if (result) + { + return await _repository.UpdateTaskItemAsync(task); + } + + return false; + } + + private bool ChangeStatus(TaskItem task, TaskItemStatus newStatus) + { switch (newStatus) { - case TaskStatus.ToDo: + case TaskItemStatus.ToDo: return task.Open(); - case TaskStatus.InProgress: + case TaskItemStatus.InProgress: return task.Start(); - case TaskStatus.Done: + case TaskItemStatus.Done: return task.Done(); default: return false; } - + } + + public async Task GetAllUsersAsync() => await _repository.GetAllUsersAsync(); + + public async Task AssignToAsync(int taskId, int? userId) + { + var task = await GetAsync(taskId); + if (task == null) + return false; + + User? user = null; + if (userId.HasValue) + { + user = await _repository.GetUserByIdAsync(userId.Value); + if (user == null) + return false; + } + task.AssignTo(user); + return await _repository.UpdateTaskItemAsync(task); } } } \ No newline at end of file diff --git a/TaskManager/BusinessLogic/User.cs b/TaskManager/BusinessLogic/User.cs new file mode 100644 index 0000000..b5c8864 --- /dev/null +++ b/TaskManager/BusinessLogic/User.cs @@ -0,0 +1,20 @@ +namespace TaskManager.BusinessLogic +{ + public class User + { + public int Id { get; } + public string Name { get; } + public List Tasks { get; } + + private User() { } + + public User(int id, string name) + { + Id = id; + Name = name; + Tasks = new List(); + } + + public override string ToString() => $"{Id}. {Name}"; + } +} \ No newline at end of file diff --git a/TaskManager/Program.cs b/TaskManager/Program.cs index 17b2dbf..111a93e 100644 --- a/TaskManager/Program.cs +++ b/TaskManager/Program.cs @@ -1,20 +1,34 @@ -using System.Text; +using System.Data.SqlClient; +using System.Text; +using Dapper; using TaskManager.BusinessLogic; -//TaskStatus już istnieje w przestrzeni System.Threading.Tasks, która jest automatycznie importowana. -//Musimy rozwiązać konflikt nazw stosując alias. -using TaskStatus = TaskManager.BusinessLogic.TaskStatus; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Serilog; -namespace TaskManager -{ - public class Program + var builder = WebApplication.CreateBuilder(args); + + builder.Host.UseSerilog((hostingContext, loggerConfiguration) => + loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration)); + + builder.Services.AddScoped(); + + var app = builder.Build(); + + public partial class Program { - private static TaskManagerService _taskManagerService = new TaskManagerService(); + private const string ConnectionString = "Server=localhost,1433;Initial Catalog=TaskManager;User ID=sa;Password=P@ssw0rd;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;"; + + private static TaskManagerService _taskManagerService = new TaskManagerService(new Repository(ConnectionString)); + private static int _createdBy = 1; - public static void Main() + public static async Task Main() { + // Console.WriteLine(await TestDbAsync()); string command; do { + Console.WriteLine("0. Wyświetl użytkowników"); Console.WriteLine("1. Dodaj zadanie"); Console.WriteLine("2. Usuń zadanie"); Console.WriteLine("3. Pokaż szczegóły zadania"); @@ -22,39 +36,56 @@ public static void Main() Console.WriteLine("5. Wyświetl zadania wg statusu"); Console.WriteLine("6. Szukaj zadania"); Console.WriteLine("7. Zmień status zadania"); - Console.WriteLine("8. Zakończ"); + Console.WriteLine("8. Przypisz zadanie"); + Console.WriteLine("9. Zakończ"); command = Console.ReadLine().Trim(); switch (command) { + case "0": + await DisplayAllUsersAsync(); + break; case "1": - AddTask(); + await AddTaskAsync(); break; case "2": - RemoveTask(); + await RemoveTaskAsync(); break; case "3": - ShowTaskDetails(); + await ShowTaskDetailsAsync(); break; case "4": - DisplayAllTasks(); + await DisplayAllTasksAsync(); break; case "5": - DisplayAllTasksByStatus(); + await DisplayAllTasksByStatusAsync(); break; case "6": - DisplaySearchedTasks(); + await DisplaySearchedTasksAsync(); break; case "7": - UpdateTaskStatus(); + await UpdateTaskStatusAsync(); break; + //case "8": + // await AssignTaskAsync(); + // break; } Console.WriteLine(""); - } while (command != "8"); + } while (command != "9"); } - private static void AddTask() + private static async Task DisplayAllUsersAsync() + { + var users = await _taskManagerService.GetAllUsersAsync(); + Console.WriteLine($"Jest {users.Length} użytkowników:"); + foreach (var user in users) + { + Console.WriteLine(user); + } + } + + private static async Task AddTaskAsync() { Console.WriteLine("Podaj opis zadania:"); var description = Console.ReadLine(); @@ -67,11 +98,11 @@ private static void AddTask() dueDate = date; } - var task = _taskManagerService.Add(description, dueDate); + var task = await _taskManagerService.AddAsync(description, _createdBy, dueDate); WriteLineSuccess($"Dodano zadanie: {task}"); } - private static void RemoveTask() + private static async Task RemoveTaskAsync() { Console.WriteLine("Podaj identyfikator zadania do usunięcia:"); int taskId; @@ -80,7 +111,7 @@ private static void RemoveTask() Console.WriteLine("Podaj identyfikator zadania do usunięcia:"); } - if (_taskManagerService.Remove(taskId)) + if (await _taskManagerService.RemoveAsync(taskId)) { WriteLineSuccess($"Usunięto zadanie o numerze {taskId}"); } @@ -90,10 +121,10 @@ private static void RemoveTask() } } - private static void ShowTaskDetails() + private static async Task ShowTaskDetailsAsync() { var taskId = ReadTaskId(); - var task = _taskManagerService.Get(taskId); + var task = await _taskManagerService.GetAsync(taskId); if (task == null) { WriteLineError($"Nie można znaleźć zadania o numerze {taskId}"); @@ -103,6 +134,8 @@ private static void ShowTaskDetails() var sb = new StringBuilder(); sb.AppendLine(task.ToString()); sb.AppendLine($" Data utworzenia: {task.CreationDate}"); + sb.AppendLine($" Utworzone przez: {task.CreatedBy}"); + sb.AppendLine($" Przypisane do: {task.AssignedTo?.ToString() ?? ""}"); sb.AppendLine($" Data spodziewanego końca: {task.DueDate}"); sb.AppendLine($" Data startu: {task.StartDate}"); sb.AppendLine($" Data zakończenia: {task.DoneDate}"); @@ -110,35 +143,35 @@ private static void ShowTaskDetails() Console.WriteLine(sb); } - private static void DisplayAllTasks() + private static async Task DisplayAllTasksAsync() { - var tasks = _taskManagerService.GetAll(); - Console.WriteLine($"Masz {tasks.Length} zadań:"); + var tasks = await _taskManagerService.GetAllAsync(); + Console.WriteLine($"Jest {tasks.Length} zadań:"); foreach (var task in tasks) { Console.WriteLine(task); } } - private static void DisplayAllTasksByStatus() + private static async Task DisplayAllTasksByStatusAsync() { - var statuses = string.Join(", ", Enum.GetNames()); + var statuses = string.Join(", ", Enum.GetNames()); Console.WriteLine($"Podaj status: {statuses}"); - TaskStatus status; - while (!Enum.TryParse(Console.ReadLine(), true, out status)) + TaskItemStatus itemStatus; + while (!Enum.TryParse(Console.ReadLine(), true, out itemStatus)) { Console.WriteLine($"Podaj status: {statuses}"); } - var tasks = _taskManagerService.GetAll(status); - Console.WriteLine($"Masz {tasks.Length} zadań ({status}):"); + var tasks = await _taskManagerService.GetAllAsync(itemStatus); + Console.WriteLine($"Jest {tasks.Length} zadań ({itemStatus}):"); foreach (var task in tasks) { Console.WriteLine(task); } } - private static void DisplaySearchedTasks() + private static async Task DisplaySearchedTasksAsync() { Console.WriteLine($"Wyszukaj zadania o treści (możesz podać fragment):"); string text; @@ -152,7 +185,7 @@ private static void DisplaySearchedTasks() } break; } - var tasks = _taskManagerService.GetAll(text); + var tasks = await _taskManagerService.GetAllAsync(text); Console.WriteLine($"Znaleziono {tasks.Length} zadań:"); foreach (var task in tasks) { @@ -160,18 +193,18 @@ private static void DisplaySearchedTasks() } } - private static void UpdateTaskStatus() + private static async Task UpdateTaskStatusAsync() { var taskId = ReadTaskId(); - var statuses = string.Join(", ", Enum.GetNames()); + var statuses = string.Join(", ", Enum.GetNames()); Console.WriteLine($"Podaj nowy status: {statuses}"); - TaskStatus status; - while (!Enum.TryParse(Console.ReadLine(), true, out status)) + TaskItemStatus itemStatus; + while (!Enum.TryParse(Console.ReadLine(), true, out itemStatus)) { Console.WriteLine($"Podaj nowy status: {statuses}"); } - if (_taskManagerService.ChangeStatus(taskId, status)) + if (await _taskManagerService.ChangeStatusAsync(taskId, itemStatus)) { WriteLineSuccess($"Zmieniono status zadania o numerze {taskId}"); } @@ -181,6 +214,21 @@ private static void UpdateTaskStatus() } } + private static async Task AssignTaskAsync() + { + var taskId = ReadTaskId(); + var userId = ReadUserId(); + + if (await _taskManagerService.AssignToAsync(taskId, userId)) + { + WriteLineSuccess($"Przypisano zadanie o numerze {taskId} do użytkownika {userId}"); + } + else + { + WriteLineError($"Nie można przypisać zadania o numerze {taskId} do użytkownika {userId}"); + } + } + private static int ReadTaskId() { Console.WriteLine("Podaj identyfikator zadania:"); @@ -192,6 +240,21 @@ private static int ReadTaskId() return taskId; } + private static int? ReadUserId() + { + while (true) + { + Console.WriteLine("Podaj identyfikator użytkownika lub pozostaw puste, aby odpiąć użytkownika od zadania:"); + var str = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(str)) + return null; + + int userId; + if (int.TryParse(str, out userId)) + return userId; + } + } + private static void WriteLineSuccess(string text) { Console.ForegroundColor = ConsoleColor.Green; @@ -205,5 +268,19 @@ private static void WriteLineError(string text) Console.WriteLine(text); Console.ResetColor(); } - } -} \ No newline at end of file + + private static async Task TestDbAsync() + { + using (var connection = new SqlConnection(ConnectionString)) + { + var sql = @"SELECT CONCAT( + 'Tabela TaskItems ' + , CASE WHEN OBJECT_ID('TaskItems', 'U') IS NOT NULL THEN 'istnieje' ELSE 'nieistnieje' END + , CHAR(13)+CHAR(10) + , CONCAT('Tabela Users ', CASE WHEN OBJECT_ID('Users', 'U') IS NOT NULL THEN 'istnieje' ELSE 'nieistnieje' END) +)"; + var result = await connection.QueryFirstAsync(sql); + return result; + } + } + } \ No newline at end of file diff --git a/TaskManager/TaskManager.csproj b/TaskManager/TaskManager.csproj index b9de063..f6c51dc 100644 --- a/TaskManager/TaskManager.csproj +++ b/TaskManager/TaskManager.csproj @@ -7,4 +7,12 @@ enable + + + + + + + +