From 2e5cd7829d2e967ca13742492d995def0c68fe82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Minelli?= <git@minelli.swiss>
Date: Tue, 25 Jun 2024 03:14:03 +0200
Subject: [PATCH] Tags => Code integration

---
 .../.idea/material_theme_project_new.xml      |   4 +-
 ExpressAPI/assets/OpenAPI/OpenAPI.yaml        |   2 +-
 .../migration.sql                             |  17 +++
 .../20240212153007_test_tag/migration.sql     |   7 -
 .../20240212153538_test_tag/migration.sql     |   1 -
 .../migration.sql                             |   1 -
 .../migration.sql                             |   1 -
 .../migration.sql                             |   7 -
 .../20240309201554_tags/migration.sql         |   1 -
 .../20240309204629_tag_type/migration.sql     |   1 -
 .../20240311140413_tag_type/migration.sql     |   1 -
 .../migration.sql                             |   8 --
 .../migration.sql                             |   8 --
 .../migration.sql                             |   8 --
 .../migration.sql                             |   1 -
 .../migration.sql                             |   8 --
 .../migration.sql                             |  17 +++
 .../migration.sql                             |   2 +
 .../migration.sql                             |   2 +
 ExpressAPI/prisma/schema.prisma               |  27 ++--
 ExpressAPI/src/managers/TagProposalManager.ts |  17 +++
 ExpressAPI/src/managers/TagSubmitManager.ts   |  16 ---
 .../src/middlewares/ParamsCallbackManager.ts  |  31 ++--
 ExpressAPI/src/routes/ApiRoutesManager.ts     |   2 +-
 ExpressAPI/src/routes/AssignmentRoutes.ts     |   3 +-
 ExpressAPI/src/routes/TagRoutes.ts            | 132 ++++++++++++++++++
 ExpressAPI/src/routes/TagsRoutes.ts           | 128 -----------------
 ExpressAPI/src/shared                         |   2 +-
 ExpressAPI/src/types/DatabaseTypes.ts         |  14 +-
 ExpressAPI/src/types/express/index.d.ts       |   7 +-
 30 files changed, 235 insertions(+), 241 deletions(-)
 rename ExpressAPI/prisma/migrations/{20240222174056_add_correction_to_assignment => 20240212153007_add_tags}/migration.sql (70%)
 delete mode 100644 ExpressAPI/prisma/migrations/20240212153007_test_tag/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240212153538_test_tag/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240212155805_description_of_the_migration/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240212155844_description_of_the_migration/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240309094026_add_correction_to_assignment/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240309201554_tags/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240309204629_tag_type/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240311140413_tag_type/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240320215213_add_correction_to_assignment/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240320220606_add_correction_to_assignment/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240321212753_add_correction_to_assignment/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240321213742_add_correction_to_assignment/migration.sql
 delete mode 100644 ExpressAPI/prisma/migrations/20240321214043_add_correction_to_assignment/migration.sql
 create mode 100644 ExpressAPI/prisma/migrations/20240619160717_rename_tag_proposal_table/migration.sql
 create mode 100644 ExpressAPI/prisma/migrations/20240619232301_set_tag_proposal_state_default/migration.sql
 create mode 100644 ExpressAPI/prisma/migrations/20240619232804_add_tag_proposal_details/migration.sql
 create mode 100644 ExpressAPI/src/managers/TagProposalManager.ts
 delete mode 100644 ExpressAPI/src/managers/TagSubmitManager.ts
 create mode 100644 ExpressAPI/src/routes/TagRoutes.ts
 delete mode 100644 ExpressAPI/src/routes/TagsRoutes.ts

diff --git a/ExpressAPI/.idea/material_theme_project_new.xml b/ExpressAPI/.idea/material_theme_project_new.xml
index 08d9320..16e830f 100644
--- a/ExpressAPI/.idea/material_theme_project_new.xml
+++ b/ExpressAPI/.idea/material_theme_project_new.xml
@@ -3,7 +3,9 @@
   <component name="MaterialThemeProjectNewConfig">
     <option name="metadata">
       <MTProjectMetadataState>
-        <option name="userId" value="104e8585:19002424fea:-7fcc" />
+        <option name="migrated" value="true" />
+        <option name="pristineConfig" value="false" />
+        <option name="userId" value="104e8585:19002424fea:-7ffe" />
       </MTProjectMetadataState>
     </option>
   </component>
diff --git a/ExpressAPI/assets/OpenAPI/OpenAPI.yaml b/ExpressAPI/assets/OpenAPI/OpenAPI.yaml
index 8ebb216..23d099e 100644
--- a/ExpressAPI/assets/OpenAPI/OpenAPI.yaml
+++ b/ExpressAPI/assets/OpenAPI/OpenAPI.yaml
@@ -1,7 +1,7 @@
 openapi: 3.1.0
 info:
     title: Dojo API
-    version: 4.1.0
+    version: 4.2.0
     description: |
         **Backend API of the Dojo project.**
         
diff --git a/ExpressAPI/prisma/migrations/20240222174056_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240212153007_add_tags/migration.sql
similarity index 70%
rename from ExpressAPI/prisma/migrations/20240222174056_add_correction_to_assignment/migration.sql
rename to ExpressAPI/prisma/migrations/20240212153007_add_tags/migration.sql
index 7c9d9ee..4760713 100644
--- a/ExpressAPI/prisma/migrations/20240222174056_add_correction_to_assignment/migration.sql
+++ b/ExpressAPI/prisma/migrations/20240212153007_add_tags/migration.sql
@@ -1,3 +1,11 @@
+-- CreateTable
+CREATE TABLE `Tag` (
+    `name` CHAR(36) NOT NULL,
+    `type` ENUM('LANGUAGE', 'FRAMEWORK', 'THEME', 'USERDEFINED') NOT NULL,
+
+    PRIMARY KEY (`name`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
 -- CreateTable
 CREATE TABLE `_AssignmentToTag` (
     `A` VARCHAR(191) NOT NULL,
@@ -27,3 +35,12 @@ ALTER TABLE `_ExerciseToTag` ADD CONSTRAINT `_ExerciseToTag_A_fkey` FOREIGN KEY
 
 -- AddForeignKey
 ALTER TABLE `_ExerciseToTag` ADD CONSTRAINT `_ExerciseToTag_B_fkey` FOREIGN KEY (`B`) REFERENCES `Tag`(`name`) ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- CreateTable
+CREATE TABLE `SubmissionTag` (
+    `name` CHAR(36) NOT NULL,
+    `type` ENUM('LANGUAGE', 'FRAMEWORK', 'THEME', 'USERDEFINED') NOT NULL,
+    `state` VARCHAR(191) NOT NULL,
+
+    PRIMARY KEY (`name`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/ExpressAPI/prisma/migrations/20240212153007_test_tag/migration.sql b/ExpressAPI/prisma/migrations/20240212153007_test_tag/migration.sql
deleted file mode 100644
index 1931dc5..0000000
--- a/ExpressAPI/prisma/migrations/20240212153007_test_tag/migration.sql
+++ /dev/null
@@ -1,7 +0,0 @@
--- CreateTable
-CREATE TABLE `Tag` (
-    `name` CHAR(36) NOT NULL,
-    `type` ENUM('LANGUAGE', 'FRAMEWORK', 'THEME', 'USERDEFINED') NOT NULL,
-
-    PRIMARY KEY (`name`)
-) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/ExpressAPI/prisma/migrations/20240212153538_test_tag/migration.sql b/ExpressAPI/prisma/migrations/20240212153538_test_tag/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240212153538_test_tag/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240212155805_description_of_the_migration/migration.sql b/ExpressAPI/prisma/migrations/20240212155805_description_of_the_migration/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240212155805_description_of_the_migration/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240212155844_description_of_the_migration/migration.sql b/ExpressAPI/prisma/migrations/20240212155844_description_of_the_migration/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240212155844_description_of_the_migration/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240309094026_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240309094026_add_correction_to_assignment/migration.sql
deleted file mode 100644
index afb678b..0000000
--- a/ExpressAPI/prisma/migrations/20240309094026_add_correction_to_assignment/migration.sql
+++ /dev/null
@@ -1,7 +0,0 @@
--- CreateTable
-CREATE TABLE `SubmissionTag` (
-    `name` CHAR(36) NOT NULL,
-    `type` ENUM('LANGUAGE', 'FRAMEWORK', 'THEME', 'USERDEFINED') NOT NULL,
-
-    PRIMARY KEY (`name`)
-) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/ExpressAPI/prisma/migrations/20240309201554_tags/migration.sql b/ExpressAPI/prisma/migrations/20240309201554_tags/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240309201554_tags/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240309204629_tag_type/migration.sql b/ExpressAPI/prisma/migrations/20240309204629_tag_type/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240309204629_tag_type/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240311140413_tag_type/migration.sql b/ExpressAPI/prisma/migrations/20240311140413_tag_type/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240311140413_tag_type/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240320215213_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240320215213_add_correction_to_assignment/migration.sql
deleted file mode 100644
index 0176ca0..0000000
--- a/ExpressAPI/prisma/migrations/20240320215213_add_correction_to_assignment/migration.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
-  Warnings:
-
-  - Added the required column `state` to the `SubmissionTag` table without a default value. This is not possible if the table is not empty.
-
-*/
--- AlterTable
-ALTER TABLE `SubmissionTag` ADD COLUMN `state` ENUM('PENDINGAPPROVAL', 'DECLINED', 'APPROVED') NOT NULL;
diff --git a/ExpressAPI/prisma/migrations/20240320220606_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240320220606_add_correction_to_assignment/migration.sql
deleted file mode 100644
index ece1aea..0000000
--- a/ExpressAPI/prisma/migrations/20240320220606_add_correction_to_assignment/migration.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
-  Warnings:
-
-  - You are about to alter the column `state` on the `SubmissionTag` table. The data in that column could be lost. The data in that column will be cast from `Enum(EnumId(3))` to `VarChar(191)`.
-
-*/
--- AlterTable
-ALTER TABLE `SubmissionTag` MODIFY `state` VARCHAR(191) NOT NULL;
diff --git a/ExpressAPI/prisma/migrations/20240321212753_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240321212753_add_correction_to_assignment/migration.sql
deleted file mode 100644
index 0ded831..0000000
--- a/ExpressAPI/prisma/migrations/20240321212753_add_correction_to_assignment/migration.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
-  Warnings:
-
-  - You are about to alter the column `state` on the `SubmissionTag` table. The data in that column could be lost. The data in that column will be cast from `VarChar(191)` to `Enum(EnumId(3))`.
-
-*/
--- AlterTable
-ALTER TABLE `SubmissionTag` MODIFY `state` ENUM('PENDINGAPPROVAL', 'DECLINED', 'APPROVED') NOT NULL;
diff --git a/ExpressAPI/prisma/migrations/20240321213742_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240321213742_add_correction_to_assignment/migration.sql
deleted file mode 100644
index af5102c..0000000
--- a/ExpressAPI/prisma/migrations/20240321213742_add_correction_to_assignment/migration.sql
+++ /dev/null
@@ -1 +0,0 @@
--- This is an empty migration.
\ No newline at end of file
diff --git a/ExpressAPI/prisma/migrations/20240321214043_add_correction_to_assignment/migration.sql b/ExpressAPI/prisma/migrations/20240321214043_add_correction_to_assignment/migration.sql
deleted file mode 100644
index ece1aea..0000000
--- a/ExpressAPI/prisma/migrations/20240321214043_add_correction_to_assignment/migration.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
-  Warnings:
-
-  - You are about to alter the column `state` on the `SubmissionTag` table. The data in that column could be lost. The data in that column will be cast from `Enum(EnumId(3))` to `VarChar(191)`.
-
-*/
--- AlterTable
-ALTER TABLE `SubmissionTag` MODIFY `state` VARCHAR(191) NOT NULL;
diff --git a/ExpressAPI/prisma/migrations/20240619160717_rename_tag_proposal_table/migration.sql b/ExpressAPI/prisma/migrations/20240619160717_rename_tag_proposal_table/migration.sql
new file mode 100644
index 0000000..5484fa4
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20240619160717_rename_tag_proposal_table/migration.sql
@@ -0,0 +1,17 @@
+/*
+  Warnings:
+
+  - You are about to drop the `SubmissionTag` table. If the table is not empty, all the data it contains will be lost.
+
+*/
+-- DropTable
+DROP TABLE `SubmissionTag`;
+
+-- CreateTable
+CREATE TABLE `TagProposal` (
+    `name` CHAR(36) NOT NULL,
+    `type` ENUM('LANGUAGE', 'FRAMEWORK', 'THEME', 'USERDEFINED') NOT NULL,
+    `state` VARCHAR(191) NOT NULL,
+
+    PRIMARY KEY (`name`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/ExpressAPI/prisma/migrations/20240619232301_set_tag_proposal_state_default/migration.sql b/ExpressAPI/prisma/migrations/20240619232301_set_tag_proposal_state_default/migration.sql
new file mode 100644
index 0000000..f0feb8c
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20240619232301_set_tag_proposal_state_default/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `TagProposal` MODIFY `state` VARCHAR(191) NOT NULL DEFAULT 'PendingApproval';
diff --git a/ExpressAPI/prisma/migrations/20240619232804_add_tag_proposal_details/migration.sql b/ExpressAPI/prisma/migrations/20240619232804_add_tag_proposal_details/migration.sql
new file mode 100644
index 0000000..2857781
--- /dev/null
+++ b/ExpressAPI/prisma/migrations/20240619232804_add_tag_proposal_details/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `TagProposal` ADD COLUMN `details` VARCHAR(191) NULL;
diff --git a/ExpressAPI/prisma/schema.prisma b/ExpressAPI/prisma/schema.prisma
index 9aac09a..69e3b26 100644
--- a/ExpressAPI/prisma/schema.prisma
+++ b/ExpressAPI/prisma/schema.prisma
@@ -15,9 +15,9 @@ enum UserRole {
 
 enum TagType {
     LANGUAGE
-	FRAMEWORK
-	THEME
-	USERDEFINED
+    FRAMEWORK
+    THEME
+    USERDEFINED
 }
 
 model User {
@@ -44,7 +44,7 @@ model Assignment {
 
     exercises Exercise[]
     staff     User[]
-    tags     Tag[]
+    tags      Tag[]
 }
 
 model Exercise {
@@ -83,15 +83,16 @@ model Result {
 }
 
 model Tag {
-    name        String   @id @db.Char(36)
-    type        TagType     
+    name String  @id @db.Char(36)
+    type TagType
 
-    assignment  Assignment[]
-    exercise    Exercise[]
+    assignments Assignment[]
+    exercises   Exercise[]
 }
 
-model SubmissionTag {
-    name        String   @id @db.Char(36)
-    type        TagType 
-    state       String
-}
\ No newline at end of file
+model TagProposal {
+    name    String  @id @db.Char(36)
+    type    TagType
+    state   String  @default("PendingApproval")
+    details String?
+}
diff --git a/ExpressAPI/src/managers/TagProposalManager.ts b/ExpressAPI/src/managers/TagProposalManager.ts
new file mode 100644
index 0000000..89607e2
--- /dev/null
+++ b/ExpressAPI/src/managers/TagProposalManager.ts
@@ -0,0 +1,17 @@
+import { TagProposal } from '@prisma/client';
+import db              from '../helpers/DatabaseHelper';
+
+
+class TagProposalManager {
+    async get(name: string | undefined = undefined): Promise<TagProposal | undefined> {
+        return await db.tagProposal.findUnique({
+                                                   where: {
+                                                       name: name
+                                                   }
+                                               }) as unknown as TagProposal ?? undefined;
+    }
+}
+
+
+export default new TagProposalManager();
+
diff --git a/ExpressAPI/src/managers/TagSubmitManager.ts b/ExpressAPI/src/managers/TagSubmitManager.ts
deleted file mode 100644
index 853096f..0000000
--- a/ExpressAPI/src/managers/TagSubmitManager.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { SubmissionTag }           from '@prisma/client';
-import db                        from '../helpers/DatabaseHelper';
-
-class TagSubmitManager {
-	async get(name: string | undefined = undefined): Promise<SubmissionTag | undefined> {
-        return await db.submissionTag.findUnique({
-                                                where  : {
-                                                    name: name
-                                                }
-                                            }) as unknown as SubmissionTag ?? undefined;
-    }
-}
-
-
-export default new TagSubmitManager();
-
diff --git a/ExpressAPI/src/middlewares/ParamsCallbackManager.ts b/ExpressAPI/src/middlewares/ParamsCallbackManager.ts
index 2ff4933..1dffaef 100644
--- a/ExpressAPI/src/middlewares/ParamsCallbackManager.ts
+++ b/ExpressAPI/src/middlewares/ParamsCallbackManager.ts
@@ -1,10 +1,10 @@
-import { Express }       from 'express-serve-static-core';
-import express           from 'express';
-import { StatusCodes }   from 'http-status-codes';
-import ExerciseManager   from '../managers/ExerciseManager';
-import AssignmentManager from '../managers/AssignmentManager';
-import TagManager        from '../managers/TagManager';
-import TagSubmitManager  from '../managers/TagSubmitManager';
+import { Express }        from 'express-serve-static-core';
+import express            from 'express';
+import { StatusCodes }    from 'http-status-codes';
+import ExerciseManager    from '../managers/ExerciseManager';
+import AssignmentManager  from '../managers/AssignmentManager';
+import TagManager         from '../managers/TagManager';
+import TagProposalManager from '../managers/TagProposalManager';
 
 
 type GetFunction = (id: string | number, ...args: Array<unknown>) => Promise<unknown>
@@ -29,9 +29,10 @@ class ParamsCallbackManager {
     initBoundParams(req: express.Request) {
         if ( !req.boundParams ) {
             req.boundParams = {
-                assignment: undefined,
-                exercise  : undefined,
-                tag       : undefined 
+                assignment : undefined,
+                exercise   : undefined,
+                tag        : undefined,
+                tagProposal: undefined
             };
         }
     }
@@ -47,14 +48,12 @@ class ParamsCallbackManager {
             members   : true,
             results   : true
         } ], 'exercise');
-        
-        this.listenParam('tagId', backend, (TagManager.get as GetFunction).bind(TagManager), [ {
 
-        } ], 'tags');
+        this.listenParam('tagName', backend, (TagManager.get as GetFunction).bind(TagManager), [ {
+            assignments: true
+        } ], 'tag');
 
-        this.listenParam('tagProposalName', backend, (TagSubmitManager.get as GetFunction).bind(TagSubmitManager), [ {
- 
-        } ], 'tags');
+        this.listenParam('tagProposalName', backend, (TagProposalManager.get as GetFunction).bind(TagProposalManager), [ {} ], 'tagProposal');
     }
 }
 
diff --git a/ExpressAPI/src/routes/ApiRoutesManager.ts b/ExpressAPI/src/routes/ApiRoutesManager.ts
index 2461b4e..1508de4 100644
--- a/ExpressAPI/src/routes/ApiRoutesManager.ts
+++ b/ExpressAPI/src/routes/ApiRoutesManager.ts
@@ -5,7 +5,7 @@ import SessionRoutes    from './SessionRoutes.js';
 import AssignmentRoutes from './AssignmentRoutes.js';
 import GitlabRoutes     from './GitlabRoutes.js';
 import ExerciseRoutes   from './ExerciseRoutes.js';
-import TagsRoutes       from './TagsRoutes';
+import TagsRoutes       from './TagRoutes';
 
 
 class AdminRoutesManager implements RoutesManager {
diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts
index 5f46129..0f06d74 100644
--- a/ExpressAPI/src/routes/AssignmentRoutes.ts
+++ b/ExpressAPI/src/routes/AssignmentRoutes.ts
@@ -112,7 +112,8 @@ class AssignmentRoutes implements RoutesManager {
                                                            include: {
                                                                assignment: false,
                                                                members   : true,
-                                                               results   : true
+                                                               results   : true,
+                                                               tags      : true
                                                            }
                                                        });
             }
diff --git a/ExpressAPI/src/routes/TagRoutes.ts b/ExpressAPI/src/routes/TagRoutes.ts
new file mode 100644
index 0000000..e7e6734
--- /dev/null
+++ b/ExpressAPI/src/routes/TagRoutes.ts
@@ -0,0 +1,132 @@
+import express, { RequestHandler } from 'express';
+import { TagType }                 from '@prisma/client';
+import * as ExpressValidator       from 'express-validator';
+import { StatusCodes }             from 'http-status-codes';
+import RoutesManager               from '../express/RoutesManager';
+import { Express }                 from 'express-serve-static-core';
+import db                          from '../helpers/DatabaseHelper';
+import SecurityCheckType           from '../types/SecurityCheckType';
+import SecurityMiddleware          from '../middlewares/SecurityMiddleware';
+import ParamsValidatorMiddleware   from '../middlewares/ParamsValidatorMiddleware';
+import DojoStatusCode              from '../shared/types/Dojo/DojoStatusCode';
+
+
+class TagRoutes implements RoutesManager {
+    private readonly tagValidator: ExpressValidator.Schema = {
+        name: {
+            trim    : true,
+            notEmpty: true
+        },
+        type: {
+            trim    : true,
+            notEmpty: true
+        }
+    };
+
+    private readonly tagProposalAnswerValidator: ExpressValidator.Schema = {
+        state  : {
+            trim    : true,
+            notEmpty: true
+        },
+        details: {
+            trim    : true,
+            optional: true
+        }
+    };
+
+    registerOnBackend(backend: Express) {
+        backend.post('/tags', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.tagValidator), this.createTag.bind(this) as RequestHandler);
+        backend.delete('/tags/:tagName', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.deleteTag.bind(this) as RequestHandler);
+        backend.get('/tags/proposals', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.getTagProposals.bind(this) as RequestHandler);
+        backend.post('/tags/proposals', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.tagValidator), this.createTagProposal.bind(this) as RequestHandler);
+        backend.patch('/tags/proposals/:tagProposalName', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), ParamsValidatorMiddleware.validate(this.tagProposalAnswerValidator), this.validateTag.bind(this) as RequestHandler);
+    }
+
+    private async createTag(req: express.Request, res: express.Response) {
+        const tagName = req.body.name;
+        const tagType = (req.body.type as string).toUpperCase() as TagType;
+
+        if ( tagType !== TagType.USERDEFINED && !req.session.profile.isAdmin ) {
+            return req.session.sendResponse(res, StatusCodes.FORBIDDEN, {}, 'Only admins can create non userDefined tags', DojoStatusCode.TAG_ONLY_ADMIN_CREATION);
+        }
+
+        await db.tag.create({
+                                data: {
+                                    name: tagName,
+                                    type: tagType
+                                }
+                            });
+
+        return req.session.sendResponse(res, StatusCodes.OK);
+    }
+
+    private async deleteTag(req: express.Request, res: express.Response) {
+        if ( req.boundParams.tag!.assignments.length > 0 ) {
+            return req.session.sendResponse(res, StatusCodes.LOCKED, {}, 'Tag is used in assignments', DojoStatusCode.TAG_WITH_ACTIVE_LINK_DELETION);
+        }
+
+        await db.tag.delete({
+                                where: { name: req.boundParams.tag!.name }
+                            });
+
+        return req.session.sendResponse(res, StatusCodes.OK);
+    }
+
+    private async getTagProposals(req: express.Request, res: express.Response) {
+        const state = req.query.stateFilter as string;
+
+        const tagProposals = await db.tagProposal.findMany(state ? {
+            where: { state: state }
+        } : {});
+
+        return req.session.sendResponse(res, StatusCodes.OK, tagProposals);
+    }
+
+    private async createTagProposal(req: express.Request, res: express.Response) {
+        const tagName = req.body.name;
+        const tagType = (req.body.type as string).toUpperCase() as TagType;
+
+        await db.tagProposal.create({
+                                        data: {
+                                            name: tagName,
+                                            type: tagType
+                                        }
+                                    });
+
+        return req.session.sendResponse(res, StatusCodes.OK);
+    }
+
+    private async validateTag(req: express.Request, res: express.Response) {
+        if ( req.boundParams.tagProposal!.state === 'PendingApproval' ) {
+            const state: string = req.body.state;
+
+            if ( state === 'Approved' ) {
+                try {
+                    await db.tag.create({
+                                            data: {
+                                                name: req.boundParams.tagProposal!.name,
+                                                type: req.boundParams.tagProposal!.type
+                                            }
+                                        });
+                } catch ( error ) {
+                    // empty
+                }
+            }
+
+            await db.tagProposal.update({
+                                            where: { name: req.boundParams.tagProposal?.name },
+                                            data : {
+                                                state  : state,
+                                                details: req.body.details ?? ''
+                                            }
+                                        });
+
+            return req.session.sendResponse(res, StatusCodes.OK);
+        } else {
+            return req.session.sendResponse(res, StatusCodes.BAD_REQUEST, {}, 'Tag proposal is not pending', DojoStatusCode.TAG_PROPOSAL_ANSWER_NOT_PENDING);
+        }
+    }
+}
+
+
+export default new TagRoutes();
diff --git a/ExpressAPI/src/routes/TagsRoutes.ts b/ExpressAPI/src/routes/TagsRoutes.ts
deleted file mode 100644
index ec13714..0000000
--- a/ExpressAPI/src/routes/TagsRoutes.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import express                        from 'express';
-import { TagType }                    from '@prisma/client';
-import * as ExpressValidator          from 'express-validator';
-import { StatusCodes }                from 'http-status-codes';
-import RoutesManager                  from '../express/RoutesManager';
-import { Express }                    from 'express-serve-static-core';
-import db                             from '../helpers/DatabaseHelper';
-import SecurityCheckType              from '../types/SecurityCheckType';
-import SecurityMiddleware             from '../middlewares/SecurityMiddleware';
-import ParamsValidatorMiddleware      from '../middlewares/ParamsValidatorMiddleware';
-
-class TagRoutes implements RoutesManager {
-    private readonly tagsValidatorNameType: ExpressValidator.Schema = {
-        name: {
-            trim: true,
-            notEmpty: true
-        },
-        type: {
-            trim: true,
-            notEmpty: true
-        }
-    };
-
-    private readonly tagsValidatorStatus: ExpressValidator.Schema = {
-        status: {
-            trim: true,
-            notEmpty: true
-        },
-    };
-
-    private readonly tagsValidatorName: ExpressValidator.Schema = {
-        tagProposalName: {
-            trim: true,
-            notEmpty: true
-        },
-    };
-
-    registerOnBackend(backend: Express) { 
-        backend.post('/tags', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.tagsValidatorNameType), this.addTag.bind(this));
-        backend.delete('/tags/:tageName', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.deleteTag.bind(this));
-        backend.get('/tags/proposals/:state', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), this.getSubmittedTag.bind(this));
-        backend.post('/tags/proposals', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.tagsValidatorNameType), this.SubmitTag.bind(this));
-        backend.patch('/tags/proposals', SecurityMiddleware.check(true, SecurityCheckType.ADMIN), ParamsValidatorMiddleware.validate(this.tagsValidatorName), this.validateTag.bind(this));
-    }
-
-    private async addTag(req: express.Request, res: express.Response) {
-        const tagName = req.body.name
-        const tagType = req.body.type
-
-        if(tagType != TagType.USERDEFINED && !req.session.profile.isAdmin) {
-            return req.session.sendResponse(res, StatusCodes.FORBIDDEN);
-        }
-
-        db.tag.upsert({
-            where : { name: tagName },
-            update: {},
-            create: {
-                name            : tagName,
-                type            : tagType,
-            }
-        })
-        return req.session.sendResponse(res, StatusCodes.OK, {
-            tag           : req.body.type,
-            name          : req.body.name
-        }, "Tag ajouté avec succès");
-    }
-    private async deleteTag(req: express.Request, res: express.Response) {
-        const tagName = req.params.name
-        
-        db.tag.delete({
-            where : { name: tagName }
-        })
-        return req.session.sendResponse(res, StatusCodes.OK, "Tag supprimé avec succès");
-    }
-    private async getSubmittedTag(req: express.Request, res: express.Response) {
-        const state = req.params.state
-
-        db.submissionTag.findMany({
-            where : { 
-                state: state 
-            }
-        })
-        return req.session.sendResponse(res, StatusCodes.OK);
-    }
-    private async SubmitTag(req: express.Request, res: express.Response) {
-        const tagName = req.body.name
-        const tagType = req.body.type
-
-        db.submissionTag.upsert({
-            where : { name: tagName },
-            update: {},
-            create: {
-                name            : tagName,
-                type            : tagType,
-                state           : "PendingApproval"
-            }
-        })
-
-        return req.session.sendResponse(res, StatusCodes.OK, {
-            name          : req.body.name,
-            tag           : req.body.tag
-        });        
-    }
-    private async validateTag(req: express.Request, res: express.Response) {
-        const state = req.body.state
-
-        if(state == "PendingApproval"){
-            return req.session.sendResponse(res, StatusCodes.OK, "Approbation toujours en attente"); 
-        } else if (state == "Declined"){
-            const detail = req.body.details
-            return req.session.sendResponse(res, StatusCodes.OK, detail); 
-        } else{
-            const tagName = req.body.tagProposalName
-            const tagType = req.body.type
-            db.tag.upsert({
-                where : { name: tagName },
-                update: {},
-                create: {
-                    name            : tagName,
-                    type            : tagType,
-                }
-            })
-        }
-        return req.session.sendResponse(res, StatusCodes.OK, "Tag accepté"); 
-    }
-}
-
-export default new TagRoutes();
diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared
index c2afa86..708a3c0 160000
--- a/ExpressAPI/src/shared
+++ b/ExpressAPI/src/shared
@@ -1 +1 @@
-Subproject commit c2afa861bf6306ddec79ffd465a4c7b0edcd3453
+Subproject commit 708a3c0805fb2b2d853a781bde86a10d5282545a
diff --git a/ExpressAPI/src/types/DatabaseTypes.ts b/ExpressAPI/src/types/DatabaseTypes.ts
index 24b8721..2a7109c 100644
--- a/ExpressAPI/src/types/DatabaseTypes.ts
+++ b/ExpressAPI/src/types/DatabaseTypes.ts
@@ -26,12 +26,12 @@ const resultBase = Prisma.validator<Prisma.ResultDefaultArgs>()({
                                                                         exercise: true
                                                                     }
                                                                 });
-const tagsBase = Prisma.validator<Prisma.TagDefaultArgs>()({
-                                                                    include: {
-                                                                        exercise: true,
-                                                                        assignment: true,
-                                                                    }
-                                                                });
+const tagBase = Prisma.validator<Prisma.TagDefaultArgs>()({
+                                                              include: {
+                                                                  exercises  : true,
+                                                                  assignments: true
+                                                              }
+                                                          });
 
 
 export type User = Prisma.UserGetPayload<typeof userBase> & {
@@ -46,4 +46,4 @@ export type Assignment = Prisma.AssignmentGetPayload<typeof assignmentBase> & {
     corrections: LazyVal<Array<Exercise>>
 }
 export type Result = Prisma.ResultGetPayload<typeof resultBase>
-export type Tags = Prisma.TagGetPayload<typeof tagsBase>
\ No newline at end of file
+export type Tag = Prisma.TagGetPayload<typeof tagBase>
\ No newline at end of file
diff --git a/ExpressAPI/src/types/express/index.d.ts b/ExpressAPI/src/types/express/index.d.ts
index 815c382..59a5e00 100644
--- a/ExpressAPI/src/types/express/index.d.ts
+++ b/ExpressAPI/src/types/express/index.d.ts
@@ -1,5 +1,6 @@
-import Session                  from '../../controllers/Session.js';
-import { Assignment, Exercise, Tags } from '../DatabaseTypes';
+import Session                       from '../../controllers/Session.js';
+import { Assignment, Exercise, Tag } from '../DatabaseTypes';
+import { TagProposal }               from '@prisma/client';
 
 // to make the file a module and avoid the TypeScript error
 export {};
@@ -9,7 +10,7 @@ declare global {
         export interface Request {
             session: Session,
             boundParams: {
-                assignment: Assignment | undefined, exercise: Exercise | undefined, tag: Tags | undefined
+                assignment: Assignment | undefined, exercise: Exercise | undefined, tag: Tag | undefined, tagProposal: TagProposal | undefined
             }
         }
     }
-- 
GitLab