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