diff --git a/ExpressAPI/.idea/material_theme_project_new.xml b/ExpressAPI/.idea/material_theme_project_new.xml index 08d93208ec31f4c00760f273384c3ace69519b66..16e830f2d917c0815e814dcbf57e219c3b85c2e2 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 8ebb21662faad48639bce22286e04c0d2f7c4450..23d099e0a7b64cb6a9bff5c5fe886336cbe3d1d8 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 7c9d9ee91b6b11784c6e9f4130be1918aaf5e886..4760713466e16ff80d81a734fe2f3c31837eadcc 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 1931dc55ba39c69d729213d25294057ba262c76b..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 afb678b3703c2917b030524c6f2f23762b59f6f7..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 0176ca0291e986695b40eff88fb3ef0a4b0fab73..0000000000000000000000000000000000000000 --- 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 ece1aead8605a0456ed1ad59f96076e7c4231fd4..0000000000000000000000000000000000000000 --- 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 0ded83188955e133b20f23b0030964055ce3cdb0..0000000000000000000000000000000000000000 --- 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 af5102c8ba20cc6051c01d3ca06b2e7f8d7e14d9..0000000000000000000000000000000000000000 --- 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 ece1aead8605a0456ed1ad59f96076e7c4231fd4..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..5484fa4855165f86579d571d91ec421ee13ab5c9 --- /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 0000000000000000000000000000000000000000..f0feb8cccc7b2e7466528d133231457418edd075 --- /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 0000000000000000000000000000000000000000..28577819d43b5e17369af23ae3e9ee4efb43edfc --- /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 9aac09a10024dc8edc3f36cdca7d7dceccb0bf87..69e3b2678b3e6e6548bc86c6f713b5560ccc31f0 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 0000000000000000000000000000000000000000..89607e24c566a377b3abc0b1e5ac66c5b1129874 --- /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 853096f486de358b19c1cc917cd50d315201f3ec..0000000000000000000000000000000000000000 --- 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 2ff49332b5e3686dd76f14d6b1af8b328da8526f..1dffaef8a782bb06c01e485705afc830e563f441 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 2461b4eb55b5f010b46f7e791c9bf6d0228ab52e..1508de4124baa104b7aac892da229685ecb9b998 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 5f46129df7cf916d4b7a94f17bb108f32289ee4c..0f06d74728a684153763d8b7d710809ff6f6e9f4 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 0000000000000000000000000000000000000000..e7e673403fe94dc209d53a91616c7140bc0ea1ec --- /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 ec13714404a97f9a4743bab34541e6d956d91ddc..0000000000000000000000000000000000000000 --- 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 c2afa861bf6306ddec79ffd465a4c7b0edcd3453..708a3c0805fb2b2d853a781bde86a10d5282545a 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 24b87219bdd7bdd9f03a79e0fdcf738a27f23085..2a7109c532b14733f5f32328757d922a404821b7 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 815c382d84b69c18b9e2db72135827d936607d36..59a5e0056b7d85185e11d49a66d82153cab01192 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 } } }