commit 162db5f6c0ffbda65c261acfb766e7a56239d327
Author: Vojtěch Bartička <aitakaitov@gmail.com>
Date:   Thu Apr 21 13:54:25 2022 +0200

    Added endpoints for Tag CRUD

diff --git a/Backend/Backend/Controllers/TagController.cs b/Backend/Backend/Controllers/TagController.cs
index 5ec1318..9216b5e 100644
--- a/Backend/Backend/Controllers/TagController.cs
+++ b/Backend/Backend/Controllers/TagController.cs
@@ -94,5 +94,59 @@ public class TagController : Common.CommonControllerBase
 
         return Ok();
     }
+
+    [HttpPost("/tags")]
+    [Authorize(Models.Enums.ERole.ADMINISTRATOR)]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+    public ActionResult CreateTag([FromBody] CreateTagRequest request)
+    {
+        try
+        {
+            tagService.CreateTag(request);
+        }
+        catch (InvalidOperationException e)
+        {
+            throw new BadRequestException(e.Message);
+        }
+
+        return Ok();
+    }
+
+    [HttpDelete("/tag/{tagId}")]
+    [Authorize(Models.Enums.ERole.ADMINISTRATOR)]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+    public ActionResult DeleteTag(Guid tagId)
+    {
+        try
+        {
+            tagService.DeleteTag(tagId);
+        }
+        catch (InvalidOperationException e)
+        {
+            throw new BadRequestException(e.Message);
+        }
+
+        return Ok();
+    }
+
+    [HttpPut("/tag/{tagId}")]
+    [Authorize(Models.Enums.ERole.ADMINISTRATOR)]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+    public ActionResult UpdateTag([FromBody] ModifyTagRequest request, Guid tagId)
+    {
+        try
+        {
+            tagService.UpdateTag(request, tagId);
+        }
+        catch (InvalidOperationException e)
+        {
+            throw new BadRequestException(e.Message);
+        }
+
+        return Ok();
+    }
 }
 
diff --git a/Backend/Core/Services/TagService/ITagService.cs b/Backend/Core/Services/TagService/ITagService.cs
index 455dfc0..b8a2f59 100644
--- a/Backend/Core/Services/TagService/ITagService.cs
+++ b/Backend/Core/Services/TagService/ITagService.cs
@@ -13,9 +13,9 @@ namespace Core.Services.TagService
         public void CreateCategory(CreateCategoryRequest request);
         public void DeleteCategory(Guid categoryId);
         public void UpdateCategory(ModifyCategoryRequest request, Guid categoryId);
-        //public void CreateTag();
-        //public void DeleteTag();
-        //public void UpdateTag();
+        public void CreateTag(CreateTagRequest request);
+        public void DeleteTag(Guid tagId);
+        public void UpdateTag(ModifyTagRequest request, Guid tagId);
         //public void CreateSubTag();
         //public void DeleteSubTag();
         //public void UpdateSubTag();
diff --git a/Backend/Core/Services/TagService/TagServiceEF.cs b/Backend/Core/Services/TagService/TagServiceEF.cs
index 38b941b..351b597 100644
--- a/Backend/Core/Services/TagService/TagServiceEF.cs
+++ b/Backend/Core/Services/TagService/TagServiceEF.cs
@@ -71,10 +71,7 @@ namespace Core.Services.TagService
                 throw new InvalidOperationException("Category name or color empty");
             }
 
-            if (databaseContext.TagCategories.Any(tc => tc.Name == request.Name))
-            {
-                throw new InvalidOperationException("Category name already used");
-            }
+            CategoryNameUnusedOrThrow(request.Name);
 
             databaseContext.TagCategories.Add(new Entities.TagCategory()
             {
@@ -102,10 +99,7 @@ namespace Core.Services.TagService
 
             if (request.Name != null)
             {
-                if (databaseContext.TagCategories.Any(tc => tc.Name == request.Name))
-                {
-                    throw new InvalidOperationException("Category name is already used");
-                }
+                CategoryNameUnusedOrThrow(request.Name);
                 category.Name = request.Name;
             }
 
@@ -126,7 +120,64 @@ namespace Core.Services.TagService
             databaseContext.SaveChanges();
         }
 
+        public void CreateTag(CreateTagRequest request)
+        {
+            CategoryExistsOrThrow(request.CategoryId);
 
+            TagNameUnusedOrThrow(request.Name);
+
+            if (request.Color == "")
+            {
+                throw new InvalidOperationException("Color empty");
+            }
+
+            var category = databaseContext.TagCategories.First(tc => tc.Id == request.CategoryId);
+
+            databaseContext.Tags.Add(new Entities.Tag()
+            {
+                Name = request.Name,
+                Color = request.Color,
+                Description = request.Description,
+                Category = category
+            });
+
+            databaseContext.SaveChanges();
+        }
+
+        public void DeleteTag(Guid tagId)
+        {
+            TagExistsOrThrow(tagId);
+            var tag = databaseContext.Tags.First(t => t.Id == tagId);
+            databaseContext.Tags.Remove(tag);
+        }
+
+        public void UpdateTag(ModifyTagRequest request, Guid tagId)
+        {
+            TagExistsOrThrow(tagId);
+            var tag = databaseContext.Tags.First(t => t.Id == tagId);
+
+            if (request.Name != null)
+            {
+                TagNameUnusedOrThrow(request.Name);
+                tag.Name = request.Name;
+            }
+
+            if (request.Color != null)
+            {
+                if (request.Color == "")
+                {
+                    throw new InvalidOperationException("Color empty");
+                }
+                tag.Color = request.Color;
+            }
+
+            if (request.Description != null)
+            {
+                tag.Description = request.Description;
+            }
+
+            databaseContext.SaveChanges();
+        }
 
 
 
@@ -140,5 +191,29 @@ namespace Core.Services.TagService
                 throw new InvalidOperationException("Category does not exist");
             }
         }
+
+        private void CategoryNameUnusedOrThrow(string name)
+        {
+            if (databaseContext.TagCategories.Any(tc => tc.Name == name))
+            {
+                throw new InvalidOperationException("Category name already used");
+            }
+        }
+
+        private void TagExistsOrThrow(Guid tagId)
+        {
+            if (!databaseContext.Tags.Any(t => t.Id == tagId))
+            {
+                throw new InvalidOperationException("Tag does not exist");
+            }
+        }
+
+        private void TagNameUnusedOrThrow(string name)
+        {
+            if (databaseContext.Tags.Any(t => t.Name == name))
+            {
+                throw new InvalidOperationException("Tag name already used");
+            }
+        }
     }
 }
diff --git a/Backend/Models/Tags/CreateTagRequest.cs b/Backend/Models/Tags/CreateTagRequest.cs
new file mode 100644
index 0000000..fc161d8
--- /dev/null
+++ b/Backend/Models/Tags/CreateTagRequest.cs
@@ -0,0 +1,16 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Models.Tags
+{
+    public class CreateTagRequest
+    {
+        public Guid CategoryId { get; set; }
+        public string Name { get; set; }
+        public string Description { get; set; } = "";
+        public string Color { get; set; }
+    }
+}
diff --git a/Backend/Models/Tags/ModifyTagRequest.cs b/Backend/Models/Tags/ModifyTagRequest.cs
new file mode 100644
index 0000000..df67e34
--- /dev/null
+++ b/Backend/Models/Tags/ModifyTagRequest.cs
@@ -0,0 +1,15 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Models.Tags
+{
+    public class ModifyTagRequest
+    {
+        public string? Name { get; set; }
+        public string? Description { get; set; }
+        public string? Color { get; set; }
+    }
+}
