commit 1a65a2c228668282efcd31e2aaa5a99a421a6346
Author: Vojtěch Bartička <aitakaitov@gmail.com>
Date:   Thu Apr 21 14:24:57 2022 +0200

    Added endpoints for subtag CRUD

diff --git a/Backend/Backend/Controllers/TagController.cs b/Backend/Backend/Controllers/TagController.cs
index 9216b5e..a55daa6 100644
--- a/Backend/Backend/Controllers/TagController.cs
+++ b/Backend/Backend/Controllers/TagController.cs
@@ -148,5 +148,59 @@ public class TagController : Common.CommonControllerBase
 
         return Ok();
     }
+
+    [HttpPost("/subtags")]
+    [Authorize(Models.Enums.ERole.ADMINISTRATOR)]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+    public ActionResult CreateSubTag([FromBody] CreateSubTagRequest request)
+    {
+        try
+        {
+            tagService.CreateSubTag(request);
+        }
+        catch (InvalidOperationException e)
+        {
+            throw new BadRequestException(e.Message);
+        }
+
+        return Ok();
+    }
+
+    [HttpDelete("/subtag/{subtagId}")]
+    [Authorize(Models.Enums.ERole.ADMINISTRATOR)]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+    public ActionResult DeleteSubTag(Guid subtagId)
+    {
+        try
+        {
+            tagService.DeleteSubTag(subtagId);
+        }
+        catch (InvalidOperationException e)
+        {
+            throw new BadRequestException(e.Message);
+        }
+
+        return Ok();
+    }
+
+    [HttpPut("/subtag/{subtagId}")]
+    [Authorize(Models.Enums.ERole.ADMINISTRATOR)]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    [ProducesResponseType((int)HttpStatusCode.Forbidden)]
+    public ActionResult UpdateSubTag([FromBody] ModifySubTagRequest request, Guid subtagId)
+    {
+        try
+        {
+            tagService.UpdateSubTag(request, subtagId);
+        }
+        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 b8a2f59..0183fe5 100644
--- a/Backend/Core/Services/TagService/ITagService.cs
+++ b/Backend/Core/Services/TagService/ITagService.cs
@@ -16,8 +16,8 @@ namespace Core.Services.TagService
         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();
+        public void CreateSubTag(CreateSubTagRequest request);
+        public void DeleteSubTag(Guid subtagId);
+        public void UpdateSubTag(ModifySubTagRequest request, Guid subtagId);
     }
 }
diff --git a/Backend/Core/Services/TagService/TagServiceEF.cs b/Backend/Core/Services/TagService/TagServiceEF.cs
index 351b597..6081119 100644
--- a/Backend/Core/Services/TagService/TagServiceEF.cs
+++ b/Backend/Core/Services/TagService/TagServiceEF.cs
@@ -71,7 +71,7 @@ namespace Core.Services.TagService
                 throw new InvalidOperationException("Category name or color empty");
             }
 
-            CategoryNameUnusedOrThrow(request.Name);
+            CategoryNameUnusedElseThrow(request.Name);
 
             databaseContext.TagCategories.Add(new Entities.TagCategory()
             {
@@ -85,7 +85,7 @@ namespace Core.Services.TagService
 
         public void DeleteCategory(Guid categoryId)
         {
-            CategoryExistsOrThrow(categoryId);
+            CategoryExistsElseThrow(categoryId);
 
             var category = databaseContext.TagCategories.First(tc => tc.Id == categoryId);
             databaseContext.TagCategories.Remove(category);
@@ -94,12 +94,12 @@ namespace Core.Services.TagService
 
         public void UpdateCategory(ModifyCategoryRequest request, Guid categoryId)
         {
-            CategoryExistsOrThrow(categoryId);
+            CategoryExistsElseThrow(categoryId);
             var category = databaseContext.TagCategories.First(tc => tc.Id == categoryId);
 
             if (request.Name != null)
             {
-                CategoryNameUnusedOrThrow(request.Name);
+                CategoryNameUnusedElseThrow(request.Name);
                 category.Name = request.Name;
             }
 
@@ -122,9 +122,9 @@ namespace Core.Services.TagService
 
         public void CreateTag(CreateTagRequest request)
         {
-            CategoryExistsOrThrow(request.CategoryId);
+            CategoryExistsElseThrow(request.CategoryId);
 
-            TagNameUnusedOrThrow(request.Name);
+            TagNameUnusedElseThrow(request.Name);
 
             if (request.Color == "")
             {
@@ -146,19 +146,20 @@ namespace Core.Services.TagService
 
         public void DeleteTag(Guid tagId)
         {
-            TagExistsOrThrow(tagId);
+            TagExistsElseThrow(tagId);
             var tag = databaseContext.Tags.First(t => t.Id == tagId);
             databaseContext.Tags.Remove(tag);
+            databaseContext.SaveChanges();
         }
 
         public void UpdateTag(ModifyTagRequest request, Guid tagId)
         {
-            TagExistsOrThrow(tagId);
+            TagExistsElseThrow(tagId);
             var tag = databaseContext.Tags.First(t => t.Id == tagId);
 
             if (request.Name != null)
             {
-                TagNameUnusedOrThrow(request.Name);
+                TagNameUnusedElseThrow(request.Name);
                 tag.Name = request.Name;
             }
 
@@ -179,12 +180,60 @@ namespace Core.Services.TagService
             databaseContext.SaveChanges();
         }
 
+        public void CreateSubTag(CreateSubTagRequest request)
+        {
+            if (request.Name == "")
+            {
+                throw new InvalidOperationException("Subtag name empty");
+            }
 
+            SubTagNameUnusedElseThrow(request.Name);
+            
+            TagExistsElseThrow(request.TagId);
+            var tag = databaseContext.Tags.First(t => t.Id == request.TagId);
 
+            databaseContext.SubTags.Add(new Entities.SubTag()
+            {
+                Name = request.Name,
+                Description = request.Description,
+                Tag = tag
+            });
 
+            databaseContext.SaveChanges();
+        }
 
+        public void DeleteSubTag(Guid subtagId)
+        {
+            SubTagExistsElseThrow(subtagId);
+            var subtag = databaseContext.SubTags.First(st => st.Id == subtagId);
+            databaseContext.SubTags.Remove(subtag);
+            databaseContext.SaveChanges();
+        }
 
-        private void CategoryExistsOrThrow(Guid categoryId)
+        public void UpdateSubTag(ModifySubTagRequest request, Guid subtagId)
+        {
+            SubTagExistsElseThrow(subtagId);
+            var subtag = databaseContext.SubTags.First(st => st.Id == subtagId);
+
+            if (request.Name != null)
+            {
+                SubTagNameUnusedElseThrow(request.Name);
+                subtag.Name = request.Name;
+            }     
+
+            if (request.Description != null)
+            {
+                subtag.Description = request.Description;
+            }
+
+            databaseContext.SaveChanges();
+        }
+
+
+
+
+
+        private void CategoryExistsElseThrow(Guid categoryId)
         {
             if (!databaseContext.TagCategories.Any(tc => tc.Id == categoryId))
             {
@@ -192,7 +241,7 @@ namespace Core.Services.TagService
             }
         }
 
-        private void CategoryNameUnusedOrThrow(string name)
+        private void CategoryNameUnusedElseThrow(string name)
         {
             if (databaseContext.TagCategories.Any(tc => tc.Name == name))
             {
@@ -200,7 +249,7 @@ namespace Core.Services.TagService
             }
         }
 
-        private void TagExistsOrThrow(Guid tagId)
+        private void TagExistsElseThrow(Guid tagId)
         {
             if (!databaseContext.Tags.Any(t => t.Id == tagId))
             {
@@ -208,12 +257,28 @@ namespace Core.Services.TagService
             }
         }
 
-        private void TagNameUnusedOrThrow(string name)
+        private void TagNameUnusedElseThrow(string name)
         {
             if (databaseContext.Tags.Any(t => t.Name == name))
             {
                 throw new InvalidOperationException("Tag name already used");
             }
         }
+
+        private void SubTagExistsElseThrow(Guid subtagId)
+        {
+            if (!databaseContext.SubTags.Any(st => st.Id == subtagId))
+            {
+                throw new InvalidOperationException("Subtag does not exist");
+            }
+        }
+
+        private void SubTagNameUnusedElseThrow(string name)
+        {
+            if (databaseContext.SubTags.Any(st => st.Name == name))
+            {
+                throw new InvalidOperationException("Subtag name already used");
+            }
+        }
     }
 }
diff --git a/Backend/Models/Tags/CreateSubTagRequest.cs b/Backend/Models/Tags/CreateSubTagRequest.cs
new file mode 100644
index 0000000..9957d39
--- /dev/null
+++ b/Backend/Models/Tags/CreateSubTagRequest.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 CreateSubTagRequest
+    {
+        public string Name { get; set; }
+        public string Description { get; set; }
+        public Guid TagId { get; set; }
+    }
+}
diff --git a/Backend/Models/Tags/ModifySubTagRequest.cs b/Backend/Models/Tags/ModifySubTagRequest.cs
new file mode 100644
index 0000000..1b1dabe
--- /dev/null
+++ b/Backend/Models/Tags/ModifySubTagRequest.cs
@@ -0,0 +1,14 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Models.Tags
+{
+    public class ModifySubTagRequest
+    {
+        public string? Name { get; set; }
+        public string? Description { get; set; }
+    }
+}
