From bcbfda0af01c152e48e7d9f92a79043aa4df4c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Minelli?= <michael@minelli.me> Date: Wed, 13 Sep 2023 20:44:39 +0200 Subject: [PATCH] Rename enonce and exercice to assignment and exercise --- ...e_gitlab_ci.yml => exercise_gitlab_ci.yml} | 8 +- ExpressAPI/package.json | 2 +- .../migration.sql | 65 ++++++++ ExpressAPI/prisma/schema.prisma | 20 +-- ExpressAPI/src/config/Config.ts | 57 +++---- ExpressAPI/src/controllers/Session.ts | 4 +- ExpressAPI/src/helpers/DojoValidators.ts | 8 +- ExpressAPI/src/managers/AssignmentManager.ts | 39 +++++ ExpressAPI/src/managers/EnonceManager.ts | 40 ----- ...{ExerciceManager.ts => ExerciseManager.ts} | 12 +- .../src/middlewares/ParamsCallbackManager.ts | 30 ++-- .../src/middlewares/SecurityMiddleware.ts | 14 +- ExpressAPI/src/routes/ApiRoutesManager.ts | 18 +-- .../{EnonceRoutes.ts => AssignmentRoutes.ts} | 122 +++++++-------- .../{ExerciceRoutes.ts => ExerciseRoutes.ts} | 147 +++++++----------- ExpressAPI/src/routes/GitlabRoutes.ts | 4 +- ExpressAPI/src/shared | 2 +- ExpressAPI/src/types/DatabaseTypes.ts | 46 +++--- ExpressAPI/src/types/SecurityCheckType.ts | 8 +- ExpressAPI/src/types/express/index.d.ts | 6 +- 20 files changed, 324 insertions(+), 328 deletions(-) rename ExpressAPI/assets/{exercice_gitlab_ci.yml => exercise_gitlab_ci.yml} (85%) create mode 100644 ExpressAPI/prisma/migrations/20230913150641_rename_enonce_and_exercice_to_assignment_and_exercise/migration.sql create mode 100644 ExpressAPI/src/managers/AssignmentManager.ts delete mode 100644 ExpressAPI/src/managers/EnonceManager.ts rename ExpressAPI/src/managers/{ExerciceManager.ts => ExerciseManager.ts} (57%) rename ExpressAPI/src/routes/{EnonceRoutes.ts => AssignmentRoutes.ts} (54%) rename ExpressAPI/src/routes/{ExerciceRoutes.ts => ExerciseRoutes.ts} (53%) diff --git a/ExpressAPI/assets/exercice_gitlab_ci.yml b/ExpressAPI/assets/exercise_gitlab_ci.yml similarity index 85% rename from ExpressAPI/assets/exercice_gitlab_ci.yml rename to ExpressAPI/assets/exercise_gitlab_ci.yml index 9a9877f..f658fa5 100644 --- a/ExpressAPI/assets/exercice_gitlab_ci.yml +++ b/ExpressAPI/assets/exercise_gitlab_ci.yml @@ -1,6 +1,6 @@ ################################################################################################################### # DO NOT MODIFY THIS FILE -# This file is the ci/cd pipeline that will be used to test your exercice +# This file is the ci/cd pipeline that will be used to test your exercise ################################################################################################################### variables: @@ -16,13 +16,13 @@ stages: dojo: stage: dojo tags: - - dojo_exercice + - dojo_exercise services: - docker:dind image: - name: dojohesso/dojo_exercice_checker:latest + name: dojohesso/dojo_exercise_checker:latest script: - - dojo_exercice_checker + - dojo_exercise_checker artifacts: when: always paths: diff --git a/ExpressAPI/package.json b/ExpressAPI/package.json index 558242b..f15be44 100644 --- a/ExpressAPI/package.json +++ b/ExpressAPI/package.json @@ -1,7 +1,7 @@ { "name" : "dojo_backend_api", "description" : "Backend API for the Dojo Project", - "version" : "1.0.1", + "version" : "2.0.0", "license" : "", "author" : "Michaƫl Minelli <michael-jean.minelli@hesge.ch>", "main" : "app.js", diff --git a/ExpressAPI/prisma/migrations/20230913150641_rename_enonce_and_exercice_to_assignment_and_exercise/migration.sql b/ExpressAPI/prisma/migrations/20230913150641_rename_enonce_and_exercice_to_assignment_and_exercise/migration.sql new file mode 100644 index 0000000..34fe271 --- /dev/null +++ b/ExpressAPI/prisma/migrations/20230913150641_rename_enonce_and_exercice_to_assignment_and_exercise/migration.sql @@ -0,0 +1,65 @@ +-- RenameTable +RENAME TABLE `Enonce` TO `Assignment`; + +-- RenameTable +RENAME TABLE `Exercice` TO `Exercise`; + +-- RenameTable +RENAME TABLE `_EnonceToUser` TO `_AssignmentToUser`; + +-- RenameTable +RENAME TABLE `_ExerciceToUser` TO `_ExerciseToUser`; + +-- AlterTable +ALTER TABLE `Exercise` RENAME COLUMN `enonceName` TO `assignmentName`; + +-- AlterTable +ALTER TABLE `Result` RENAME COLUMN `exerciceId` TO `exerciseId`; + +-- RenameIndex +ALTER TABLE `Exercise` RENAME INDEX `Exercice_secret_key` TO `Exercise_secret_key`; + +-- RenameIndex +ALTER TABLE `Exercise` RENAME INDEX `Exercice_enonceName_fkey` TO `Exercise_assignmentName_fkey`; + +-- RenameIndex +ALTER TABLE `_ExerciseToUser` RENAME INDEX `_ExerciceToUser_AB_unique` TO `_ExerciseToUser_AB_unique`; + +-- RenameIndex +ALTER TABLE `_ExerciseToUser` RENAME INDEX `_ExerciceToUser_B_index` TO `_ExerciseToUser_B_index`; + +-- RenameIndex +ALTER TABLE `_AssignmentToUser` RENAME INDEX `_EnonceToUser_AB_unique` TO `_AssignmentToUser_AB_unique`; + +-- RenameIndex +ALTER TABLE `_AssignmentToUser` RENAME INDEX `_EnonceToUser_B_index` TO `_AssignmentToUser_B_index`; + +-- Rename foreign key +ALTER TABLE `Exercise` + DROP FOREIGN KEY `Exercice_enonceName_fkey`, + ADD CONSTRAINT `Exercise_assignmentName_fkey` FOREIGN KEY (`assignmentName`) REFERENCES `Assignment`(`name`) ON DELETE NO ACTION ON UPDATE CASCADE; + +-- Rename foreign key +ALTER TABLE `Result` + DROP FOREIGN KEY `Result_exerciceId_fkey`, + ADD CONSTRAINT `Result_exerciseId_fkey` FOREIGN KEY (`exerciseId`) REFERENCES `Exercise`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- Rename foreign key +ALTER TABLE `_AssignmentToUser` + DROP FOREIGN KEY `_EnonceToUser_A_fkey`, + ADD CONSTRAINT `_AssignmentToUser_A_fkey` FOREIGN KEY (`A`) REFERENCES `Assignment`(`name`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- Rename foreign key +ALTER TABLE `_AssignmentToUser` + DROP FOREIGN KEY `_EnonceToUser_B_fkey`, + ADD CONSTRAINT `_AssignmentToUser_B_fkey` FOREIGN KEY (`B`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- Rename foreign key +ALTER TABLE `_ExerciseToUser` + DROP FOREIGN KEY `_ExerciceToUser_A_fkey`, + ADD CONSTRAINT `_ExerciseToUser_A_fkey` FOREIGN KEY (`A`) REFERENCES `Exercise`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- Rename foreign key +ALTER TABLE `_ExerciseToUser` + DROP FOREIGN KEY `_ExerciceToUser_B_fkey`, + ADD CONSTRAINT `_ExerciseToUser_B_fkey` FOREIGN KEY (`B`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/ExpressAPI/prisma/schema.prisma b/ExpressAPI/prisma/schema.prisma index 7f3f418..df6640e 100644 --- a/ExpressAPI/prisma/schema.prisma +++ b/ExpressAPI/prisma/schema.prisma @@ -17,11 +17,11 @@ model User { role String? deleted Boolean @default(false) - enonces Enonce[] - exercices Exercice[] + assignments Assignment[] + exercises Exercise[] } -model Enonce { +model Assignment { name String @id gitlabId Int gitlabLink String @@ -30,13 +30,13 @@ model Enonce { gitlabLastInfoDate DateTime published Boolean @default(false) - exercices Exercice[] + exercises Exercise[] staff User[] } -model Exercice { +model Exercise { id String @id @db.Char(36) - enonceName String + assignmentName String name String secret String @unique @db.Char(36) gitlabId Int @@ -45,14 +45,14 @@ model Exercice { gitlabLastInfo Json @db.Json gitlabLastInfoDate DateTime - enonce Enonce @relation(fields: [enonceName], references: [name], onDelete: NoAction, onUpdate: Cascade) + assignment Assignment @relation(fields: [assignmentName], references: [name], onDelete: NoAction, onUpdate: Cascade) members User[] results Result[] } model Result { - exerciceId String @db.Char(36) + exerciseId String @db.Char(36) dateTime DateTime @default(now()) success Boolean exitCode Int @@ -60,7 +60,7 @@ model Result { results Json @db.Json files Json @db.Json - exercice Exercice @relation(fields: [exerciceId], references: [id], onDelete: Cascade, onUpdate: Cascade) + exercise Exercise @relation(fields: [exerciseId], references: [id], onDelete: Cascade, onUpdate: Cascade) - @@id([exerciceId, dateTime]) + @@id([exerciseId, dateTime]) } diff --git a/ExpressAPI/src/config/Config.ts b/ExpressAPI/src/config/Config.ts index 83edf27..a88b015 100644 --- a/ExpressAPI/src/config/Config.ts +++ b/ExpressAPI/src/config/Config.ts @@ -1,7 +1,7 @@ import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility'; -import { Exercice } from '../types/DatabaseTypes'; import path from 'path'; import fs from 'fs'; +import { Exercise } from '../types/DatabaseTypes'; class Config { @@ -18,14 +18,14 @@ class Config { }; public gitlab: { - apiURL: string; urls: Array<string>; account: { id: number; username: string; token: string; }; group: { root: number; templates: number; enonces: number; exercices: number; }; + apiURL: string; urls: Array<string>; account: { id: number; username: string; token: string; }; group: { root: number; templates: number; assignments: number; exercises: number; }; }; - public enonce: { + public assignment: { default: { description: string; initReadme: boolean; sharedRunnersEnabled: boolean; visibility: string; wikiEnabled: boolean; template: string }; baseFiles: Array<string>; filename: string }; - public exercice: { + public exercise: { maxSameName: number; resultsFolder: string, pipelineResultsFolder: string; default: { description: string; visibility: string; }; }; @@ -38,8 +38,7 @@ class Config { }; this.jwtConfig = { - secret : process.env.JWT_SECRET_KEY || '', - expiresIn: Number(process.env.SESSION_TIMEOUT || 0) + secret: process.env.JWT_SECRET_KEY || '', expiresIn: Number(process.env.SESSION_TIMEOUT || 0) }; this.permissions = { @@ -47,41 +46,23 @@ class Config { }; this.gitlab = { - apiURL : process.env.GITLAB_API_URL || '', - urls : JSON.parse(process.env.GITLAB_URLS || '[]'), - account: { - id : Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0), - username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || '', - token : process.env.GITLAB_DOJO_ACCOUNT_TOKEN || '' - }, - group : { - root : Number(process.env.GITLAB_GROUP_ROOT_ID || 0), - templates: Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), - enonces : Number(process.env.GITLAB_GROUP_ENONCES_ID || 0), - exercices: Number(process.env.GITLAB_GROUP_EXERCICES_ID || 0) + apiURL : process.env.GITLAB_API_URL || '', urls: JSON.parse(process.env.GITLAB_URLS || '[]'), account: { + id: Number(process.env.GITLAB_DOJO_ACCOUNT_ID || 0), username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || '', token: process.env.GITLAB_DOJO_ACCOUNT_TOKEN || '' + }, group: { + root: Number(process.env.GITLAB_GROUP_ROOT_ID || 0), templates: Number(process.env.GITLAB_GROUP_TEMPLATES_ID || 0), assignments: Number(process.env.GITLAB_GROUP_ASSIGNMENTS_ID || 0), exercises: Number(process.env.GITLAB_GROUP_EXERCISES_ID || 0) } }; - this.enonce = { - default : { - description : process.env.ENONCE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', - initReadme : process.env.ENONCE_DEFAULT_INIT_README?.toBoolean() ?? false, - sharedRunnersEnabled: process.env.ENONCE_DEFAULT_SHARED_RUNNERS_ENABLED?.toBoolean() ?? true, - visibility : process.env.ENONCE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE, - wikiEnabled : process.env.ENONCE_DEFAULT_WIKI_ENABLED?.toBoolean() ?? false, - template : process.env.ENONCE_DEFAULT_TEMPLATE?.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token) ?? '' - }, - baseFiles: JSON.parse(process.env.ENONCE_BASE_FILES || '[]'), - filename : process.env.ENONCE_FILENAME || '' + this.assignment = { + default : { + description: process.env.ASSIGNMENT_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', initReadme: process.env.ASSIGNMENT_DEFAULT_INIT_README?.toBoolean() ?? false, sharedRunnersEnabled: process.env.ASSIGNMENT_DEFAULT_SHARED_RUNNERS_ENABLED?.toBoolean() ?? true, visibility: process.env.ASSIGNMENT_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE, wikiEnabled: process.env.ASSIGNMENT_DEFAULT_WIKI_ENABLED?.toBoolean() ?? false, template: process.env.ASSIGNMENT_DEFAULT_TEMPLATE?.replace('{{USERNAME}}', this.gitlab.account.username).replace('{{TOKEN}}', this.gitlab.account.token) ?? '' + }, baseFiles: JSON.parse(process.env.ASSIGNMENT_BASE_FILES || '[]'), filename: process.env.ASSIGNMENT_FILENAME || '' }; - this.exercice = { - maxSameName : Number(process.env.EXERCICE_MAX_SAME_NAME || 0), - resultsFolder : process.env.EXERCICE_RESULTS_FOLDER?.convertWithEnvVars() ?? '', - pipelineResultsFolder: process.env.EXERCICE_PIPELINE_RESULTS_FOLDER ?? '', //Do not use convertWithEnvVars() because it is used in the exercice creation and muste be interpreted at exercice runtime - default : { - description: process.env.EXERCICE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', - visibility : process.env.EXERCICE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE + this.exercise = { + maxSameName: Number(process.env.EXERCISE_MAX_SAME_NAME || 0), resultsFolder: process.env.EXERCISE_RESULTS_FOLDER?.convertWithEnvVars() ?? '', pipelineResultsFolder: process.env.EXERCISE_PIPELINE_RESULTS_FOLDER ?? '', //Do not use convertWithEnvVars() because it is used in the exercise creation and muste be interpreted at exercise runtime + default : { + description: process.env.EXERCISE_DEFAULT_DESCRIPTION?.convertWithEnvVars() ?? '', visibility: process.env.EXERCISE_DEFAULT_VISIBILITY || GitlabVisibility.PRIVATE } }; @@ -89,8 +70,8 @@ class Config { this.userPasswordSaltRounds = Number(process.env.USER_PASSWORD_SALT_ROUNDS || 10); } - public getResultsFolder(exercice: Exercice): string { - const folderPath = path.join(this.exercice.resultsFolder, exercice.enonceName, exercice.id); + public getResultsFolder(exercise: Exercise): string { + const folderPath = path.join(this.exercise.resultsFolder, exercise.assignmentName, exercise.id); fs.mkdirSync(folderPath, { recursive: true }); diff --git a/ExpressAPI/src/controllers/Session.ts b/ExpressAPI/src/controllers/Session.ts index 7f71824..fd6b85f 100644 --- a/ExpressAPI/src/controllers/Session.ts +++ b/ExpressAPI/src/controllers/Session.ts @@ -4,8 +4,8 @@ import { JwtPayload } from 'jsonwebtoken'; import Config from '../config/Config'; import express from 'express'; import UserManager from '../managers/UserManager'; -import DojoResponse from '../shared/types/Dojo/DojoResponse'; import { User } from '../types/DatabaseTypes'; +import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse'; class Session { @@ -46,7 +46,7 @@ class Session { return profileJson === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {}); } - private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoResponse<T>> { + private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoBackendResponse<T>> { const profileJson = this.profile; let reasonPhrase = ''; diff --git a/ExpressAPI/src/helpers/DojoValidators.ts b/ExpressAPI/src/helpers/DojoValidators.ts index c443036..ccb2b65 100644 --- a/ExpressAPI/src/helpers/DojoValidators.ts +++ b/ExpressAPI/src/helpers/DojoValidators.ts @@ -4,7 +4,7 @@ import { CustomValidator, ErrorMessage, FieldMessageFactory, Meta } from 'expres import { BailOptions, ValidationChain } from 'express-validator/src/chain'; import GitlabManager from '../managers/GitlabManager'; import express from 'express'; -import SharedExerciceHelper from '../shared/helpers/Dojo/SharedExerciceHelper'; +import SharedExerciseHelper from '../shared/helpers/Dojo/SharedExerciseHelper'; declare type DojoMeta = Meta & { @@ -75,7 +75,7 @@ class DojoValidators { if ( template ) { return `${ Config.gitlab.urls[0].replace(/^([a-z]{3,5}:\/{2})?(.*)/, `$1${ Config.gitlab.account.username }:${ Config.gitlab.account.token }@$2`) }${ template }.git`; } else { - return Config.enonce.default.template; + return Config.assignment.default.template; } } catch ( e ) { } @@ -83,7 +83,7 @@ class DojoValidators { } }); - readonly exerciceResultsValidator = this.toValidatorSchemaOptions({ + readonly exerciseResultsValidator = this.toValidatorSchemaOptions({ bail : true, errorMessage: 'Results: not provided or invalid format', options : (_value, { @@ -93,7 +93,7 @@ class DojoValidators { return new Promise((resolve, reject) => { const results = this.getParamValue(req, path); if ( results ) { - SharedExerciceHelper.validateResultFile(results, false).isValid ? resolve(true) : reject(); + SharedExerciseHelper.validateResultFile(results, false).isValid ? resolve(true) : reject(); } else { reject(); } diff --git a/ExpressAPI/src/managers/AssignmentManager.ts b/ExpressAPI/src/managers/AssignmentManager.ts new file mode 100644 index 0000000..0f2bc54 --- /dev/null +++ b/ExpressAPI/src/managers/AssignmentManager.ts @@ -0,0 +1,39 @@ +import { Prisma } from '@prisma/client'; +import { Assignment, User } from '../types/DatabaseTypes'; +import db from '../helpers/DatabaseHelper'; + + +class AssignmentManager { + async isUserAllowedToAccessAssignment(assignment: Assignment, user: User): Promise<boolean> { + if ( !assignment.staff ) { + assignment.staff = await db.assignment.findUnique({ + where: { + name: assignment.name + } + }).staff() ?? []; + } + return assignment.staff.findIndex(staff => staff.id === user.id) !== -1; + } + + async getByName(name: string, include: Prisma.AssignmentInclude | undefined = undefined): Promise<Assignment | undefined> { + return await db.assignment.findUnique({ + where : { + name: name + }, include: include + }) as unknown as Assignment ?? undefined; + } + + getByGitlabLink(gitlabLink: string, include: Prisma.AssignmentInclude | undefined = undefined): Promise<Assignment | undefined> { + const name = gitlabLink.replace('.git', '').split('/').pop()!; + + return this.getByName(name, include); + } + + get(nameOrUrl: string, include: Prisma.AssignmentInclude | undefined = undefined): Promise<Assignment | undefined> { + // We can use the same function for both name and url because the name is the last part of the url and the name extraction from the url doesn't corrupt the name + return this.getByGitlabLink(nameOrUrl, include); + } +} + + +export default new AssignmentManager(); diff --git a/ExpressAPI/src/managers/EnonceManager.ts b/ExpressAPI/src/managers/EnonceManager.ts deleted file mode 100644 index 7c8b612..0000000 --- a/ExpressAPI/src/managers/EnonceManager.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Prisma } from '@prisma/client'; -import { Enonce, User } from '../types/DatabaseTypes'; -import db from '../helpers/DatabaseHelper'; - - -class EnonceManager { - async isUserAllowedToAccessEnonce(enonce: Enonce, user: User): Promise<boolean> { - if ( !enonce.staff ) { - enonce.staff = await db.enonce.findUnique({ - where: { - name: enonce.name - } - }).staff() ?? []; - } - return enonce.staff.findIndex(staff => staff.id === user.id) !== -1; - } - - async getByName(name: string, include: Prisma.EnonceInclude | undefined = undefined): Promise<Enonce | undefined> { - return await db.enonce.findUnique({ - where : { - name: name - }, - include: include - }) as unknown as Enonce ?? undefined; - } - - getByGitlabLink(gitlabLink: string, include: Prisma.EnonceInclude | undefined = undefined): Promise<Enonce | undefined> { - const name = gitlabLink.replace('.git', '').split('/').pop()!; - - return this.getByName(name, include); - } - - get(nameOrUrl: string, include: Prisma.EnonceInclude | undefined = undefined): Promise<Enonce | undefined> { - // We can use the same function for both name and url because the name is the last part of the url and the name extraction from the url doesn't corrupt the name - return this.getByGitlabLink(nameOrUrl, include); - } -} - - -export default new EnonceManager(); diff --git a/ExpressAPI/src/managers/ExerciceManager.ts b/ExpressAPI/src/managers/ExerciseManager.ts similarity index 57% rename from ExpressAPI/src/managers/ExerciceManager.ts rename to ExpressAPI/src/managers/ExerciseManager.ts index 698e16f..95bc487 100644 --- a/ExpressAPI/src/managers/ExerciceManager.ts +++ b/ExpressAPI/src/managers/ExerciseManager.ts @@ -1,18 +1,18 @@ import { Prisma } from '@prisma/client'; -import { Exercice } from '../types/DatabaseTypes'; +import { Exercise } from '../types/DatabaseTypes'; import db from '../helpers/DatabaseHelper'; -class ExerciceManager { - async get(id: string, include: Prisma.ExerciceInclude | undefined = undefined): Promise<Exercice | undefined> { - return await db.exercice.findUnique({ +class ExerciseManager { + async get(id: string, include: Prisma.ExerciseInclude | undefined = undefined): Promise<Exercise | undefined> { + return await db.exercise.findUnique({ where : { id: id }, include: include - }) as unknown as Exercice ?? undefined; + }) as unknown as Exercise ?? undefined; } } -export default new ExerciceManager(); +export default new ExerciseManager(); diff --git a/ExpressAPI/src/middlewares/ParamsCallbackManager.ts b/ExpressAPI/src/middlewares/ParamsCallbackManager.ts index 64a7bda..e81e172 100644 --- a/ExpressAPI/src/middlewares/ParamsCallbackManager.ts +++ b/ExpressAPI/src/middlewares/ParamsCallbackManager.ts @@ -1,8 +1,8 @@ -import { Express } from 'express-serve-static-core'; -import express from 'express'; -import { StatusCodes } from 'http-status-codes'; -import EnonceManager from '../managers/EnonceManager'; -import ExerciceManager from '../managers/ExerciceManager'; +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'; type GetFunction = (id: string | number, ...args: Array<any>) => Promise<any> @@ -27,23 +27,23 @@ class ParamsCallbackManager { initBoundParams(req: express.Request) { if ( !req.boundParams ) { req.boundParams = { - enonce : undefined, - exercice: undefined + assignment: undefined, + exercise : undefined }; } } register(backend: Express) { - this.listenParam('enonceNameOrUrl', backend, (EnonceManager.get as GetFunction).bind(EnonceManager), [ { - exercices: true, + this.listenParam('assignmentNameOrUrl', backend, (AssignmentManager.get as GetFunction).bind(AssignmentManager), [ { + exercises: true, staff : true - } ], 'enonce'); + } ], 'assignment'); - this.listenParam('exerciceId', backend, (ExerciceManager.get as GetFunction).bind(ExerciceManager), [ { - enonce : true, - members: true, - results: true - } ], 'exercice'); + this.listenParam('exerciseId', backend, (ExerciseManager.get as GetFunction).bind(ExerciseManager), [ { + assignment: true, + members : true, + results : true + } ], 'exercise'); } } diff --git a/ExpressAPI/src/middlewares/SecurityMiddleware.ts b/ExpressAPI/src/middlewares/SecurityMiddleware.ts index 9ba9c2f..cc7e067 100644 --- a/ExpressAPI/src/middlewares/SecurityMiddleware.ts +++ b/ExpressAPI/src/middlewares/SecurityMiddleware.ts @@ -2,7 +2,7 @@ import express from 'express'; import { StatusCodes } from 'http-status-codes'; import SecurityCheckType from '../types/SecurityCheckType'; import logger from '../shared/logging/WinstonLogger'; -import EnonceManager from '../managers/EnonceManager'; +import AssignmentManager from '../managers/AssignmentManager'; class SecurityMiddleware { @@ -24,14 +24,14 @@ class SecurityMiddleware { case SecurityCheckType.TEACHING_STAFF: isAllowed = isAllowed || req.session.profile.isTeachingStaff; break; - case SecurityCheckType.ENONCE_STAFF: - isAllowed = isAllowed || await EnonceManager.isUserAllowedToAccessEnonce(req.boundParams.enonce!, req.session.profile); + case SecurityCheckType.ASSIGNMENT_STAFF: + isAllowed = isAllowed || await AssignmentManager.isUserAllowedToAccessAssignment(req.boundParams.assignment!, req.session.profile); break; - case SecurityCheckType.ENONCE_IS_PUBLISHED: - isAllowed = isAllowed || (req.boundParams.enonce?.published ?? false); + case SecurityCheckType.ASSIGNMENT_IS_PUBLISHED: + isAllowed = isAllowed || (req.boundParams.assignment?.published ?? false); break; - case SecurityCheckType.EXERCICE_SECRET: - isAllowed = isAllowed || req.headers.authorization?.replace('ExerciceSecret ', '') === req.boundParams.exercice!.secret; + case SecurityCheckType.EXERCISE_SECRET: + isAllowed = isAllowed || req.headers.authorization?.replace('ExerciseSecret ', '') === req.boundParams.exercise!.secret; break; default: break; diff --git a/ExpressAPI/src/routes/ApiRoutesManager.ts b/ExpressAPI/src/routes/ApiRoutesManager.ts index d7641bc..ec4eeb9 100644 --- a/ExpressAPI/src/routes/ApiRoutesManager.ts +++ b/ExpressAPI/src/routes/ApiRoutesManager.ts @@ -1,10 +1,10 @@ -import { Express } from 'express-serve-static-core'; -import RoutesManager from '../express/RoutesManager'; -import BaseRoutes from './BaseRoutes'; -import SessionRoutes from './SessionRoutes'; -import EnonceRoutes from './EnonceRoutes'; -import GitlabRoutes from './GitlabRoutes'; -import ExerciceRoutes from './ExerciceRoutes'; +import { Express } from 'express-serve-static-core'; +import RoutesManager from '../express/RoutesManager'; +import BaseRoutes from './BaseRoutes'; +import SessionRoutes from './SessionRoutes'; +import AssignmentRoutes from './AssignmentRoutes'; +import GitlabRoutes from './GitlabRoutes'; +import ExerciseRoutes from './ExerciseRoutes'; class AdminRoutesManager implements RoutesManager { @@ -12,8 +12,8 @@ class AdminRoutesManager implements RoutesManager { BaseRoutes.registerOnBackend(backend); SessionRoutes.registerOnBackend(backend); GitlabRoutes.registerOnBackend(backend); - EnonceRoutes.registerOnBackend(backend); - ExerciceRoutes.registerOnBackend(backend); + AssignmentRoutes.registerOnBackend(backend); + ExerciseRoutes.registerOnBackend(backend); } } diff --git a/ExpressAPI/src/routes/EnonceRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts similarity index 54% rename from ExpressAPI/src/routes/EnonceRoutes.ts rename to ExpressAPI/src/routes/AssignmentRoutes.ts index ddff650..af42e42 100644 --- a/ExpressAPI/src/routes/EnonceRoutes.ts +++ b/ExpressAPI/src/routes/AssignmentRoutes.ts @@ -17,62 +17,55 @@ import logger from '../shared/logging/WinstonLogger'; import DojoValidators from '../helpers/DojoValidators'; import { Prisma } from '@prisma/client'; import db from '../helpers/DatabaseHelper'; -import { Enonce } from '../types/DatabaseTypes'; -import EnonceManager from '../managers/EnonceManager'; +import { Assignment } from '../types/DatabaseTypes'; +import AssignmentManager from '../managers/AssignmentManager'; import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility'; -class EnonceRoutes implements RoutesManager { - private readonly enonceValidator: ExpressValidator.Schema = { - name : { - trim : true, - notEmpty: true - }, - members : { - trim : true, - notEmpty : true, - customSanitizer: DojoValidators.jsonSanitizer - }, - template: { - trim : true, - custom : DojoValidators.templateUrlValidator, - customSanitizer: DojoValidators.templateUrlSanitizer +class AssignmentRoutes implements RoutesManager { + private readonly assignmentValidator: ExpressValidator.Schema = { + name : { + trim: true, notEmpty: true + }, members : { + trim: true, notEmpty: true, customSanitizer: DojoValidators.jsonSanitizer + }, template: { + trim: true, custom: DojoValidators.templateUrlValidator, customSanitizer: DojoValidators.templateUrlSanitizer } }; registerOnBackend(backend: Express) { - backend.get('/enonces/:enonceNameOrUrl', SecurityMiddleware.check(true), this.getEnonce); - backend.post('/enonces', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.enonceValidator), this.createEnonce); + backend.get('/assignments/:assignmentNameOrUrl', SecurityMiddleware.check(true), this.getAssignment); + backend.post('/assignments', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.assignmentValidator), this.createAssignment); - backend.patch('/enonces/:enonceNameOrUrl/publish', SecurityMiddleware.check(true, SecurityCheckType.ENONCE_STAFF), this.changeEnoncePublishedStatus(true)); - backend.patch('/enonces/:enonceNameOrUrl/unpublish', SecurityMiddleware.check(true, SecurityCheckType.ENONCE_STAFF), this.changeEnoncePublishedStatus(false)); + backend.patch('/assignments/:assignmentNameOrUrl/publish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.changeAssignmentPublishedStatus(true)); + backend.patch('/assignments/:assignmentNameOrUrl/unpublish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.changeAssignmentPublishedStatus(false)); } - // Get an enonce by its name or gitlab url - private async getEnonce(req: express.Request, res: express.Response) { - const enonce: Enonce | undefined = req.boundParams.enonce; + // Get an assignment by its name or gitlab url + private async getAssignment(req: express.Request, res: express.Response) { + const assignment: Assignment | undefined = req.boundParams.assignment; - if ( enonce && !enonce.published && !await EnonceManager.isUserAllowedToAccessEnonce(enonce, req.session.profile) ) { + if ( assignment && !assignment.published && !await AssignmentManager.isUserAllowedToAccessAssignment(assignment, req.session.profile) ) { // @ts-ignore - delete enonce.gitlabId; + delete assignment.gitlabId; // @ts-ignore - delete enonce.gitlabLink; + delete assignment.gitlabLink; // @ts-ignore - delete enonce.gitlabCreationInfo; + delete assignment.gitlabCreationInfo; // @ts-ignore - delete enonce.gitlabLastInfo; + delete assignment.gitlabLastInfo; // @ts-ignore - delete enonce.gitlabLastInfoDate; + delete assignment.gitlabLastInfoDate; // @ts-ignore - delete enonce.staff; + delete assignment.staff; // @ts-ignore - delete enonce.exercices; + delete assignment.exercises; } - return enonce ? req.session.sendResponse(res, StatusCodes.OK, enonce) : res.status(StatusCodes.NOT_FOUND).send(); + return assignment ? req.session.sendResponse(res, StatusCodes.OK, assignment) : res.status(StatusCodes.NOT_FOUND).send(); } - private async createEnonce(req: express.Request, res: express.Response) { + private async createAssignment(req: express.Request, res: express.Response) { const params: { name: string, members: Array<GitlabUser>, template: string } = req.body; @@ -82,7 +75,7 @@ class EnonceRoutes implements RoutesManager { let repository: GitlabRepository; try { - repository = await GitlabManager.createRepository(params.name, Config.enonce.default.description.replace('{{ENONCE_NAME}}', params.name), Config.enonce.default.visibility, Config.enonce.default.initReadme, Config.gitlab.group.enonces, Config.enonce.default.sharedRunnersEnabled, Config.enonce.default.wikiEnabled, params.template); + repository = await GitlabManager.createRepository(params.name, Config.assignment.default.description.replace('{{ASSIGNMENT_NAME}}', params.name), Config.assignment.default.visibility, Config.assignment.default.initReadme, Config.gitlab.group.assignments, Config.assignment.default.sharedRunnersEnabled, Config.assignment.default.wikiEnabled, params.template); await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER); } catch ( error ) { @@ -107,31 +100,23 @@ class EnonceRoutes implements RoutesManager { } })); - const enonce: Enonce = await db.enonce.create({ - data: { - name : repository.name, - gitlabId : repository.id, - gitlabLink : repository.web_url, - gitlabCreationInfo: repository as unknown as Prisma.JsonObject, - gitlabLastInfo : repository as unknown as Prisma.JsonObject, - gitlabLastInfoDate: new Date(), - staff : { - connectOrCreate: [ ...params.members.map(gitlabUser => { - return { - create: { - gitlabId : gitlabUser.id, - firstname: gitlabUser.name - }, - where : { - gitlabId: gitlabUser.id + const assignment: Assignment = await db.assignment.create({ + data: { + name: repository.name, gitlabId: repository.id, gitlabLink: repository.web_url, gitlabCreationInfo: repository as unknown as Prisma.JsonObject, gitlabLastInfo: repository as unknown as Prisma.JsonObject, gitlabLastInfoDate: new Date(), staff: { + connectOrCreate: [ ...params.members.map(gitlabUser => { + return { + create : { + gitlabId: gitlabUser.id, firstname: gitlabUser.name + }, where: { + gitlabId: gitlabUser.id + } + }; + }) ] } - }; - }) ] - } - } - }) as unknown as Enonce; + } + }) as unknown as Assignment; - return req.session.sendResponse(res, StatusCodes.OK, enonce); + return req.session.sendResponse(res, StatusCodes.OK, assignment); } catch ( error ) { if ( error instanceof AxiosError ) { return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); @@ -142,19 +127,18 @@ class EnonceRoutes implements RoutesManager { } } - private changeEnoncePublishedStatus(publish: boolean): (req: express.Request, res: express.Response) => Promise<void> { + private changeAssignmentPublishedStatus(publish: boolean): (req: express.Request, res: express.Response) => Promise<void> { return async (req: express.Request, res: express.Response): Promise<void> => { try { - await GitlabManager.changeRepositoryVisibility(req.boundParams.enonce!.gitlabId, publish ? GitlabVisibility.INTERNAL : GitlabVisibility.PRIVATE); + await GitlabManager.changeRepositoryVisibility(req.boundParams.assignment!.gitlabId, publish ? GitlabVisibility.INTERNAL : GitlabVisibility.PRIVATE); - await db.enonce.update({ - where: { - name: req.boundParams.enonce!.name - }, - data : { - published: publish - } - }); + await db.assignment.update({ + where : { + name: req.boundParams.assignment!.name + }, data: { + published: publish + } + }); req.session.sendResponse(res, StatusCodes.OK); } catch ( error ) { @@ -172,4 +156,4 @@ class EnonceRoutes implements RoutesManager { } -export default new EnonceRoutes(); +export default new AssignmentRoutes(); diff --git a/ExpressAPI/src/routes/ExerciceRoutes.ts b/ExpressAPI/src/routes/ExerciseRoutes.ts similarity index 53% rename from ExpressAPI/src/routes/ExerciceRoutes.ts rename to ExpressAPI/src/routes/ExerciseRoutes.ts index f4a2e38..e6f60a5 100644 --- a/ExpressAPI/src/routes/ExerciceRoutes.ts +++ b/ExpressAPI/src/routes/ExerciseRoutes.ts @@ -16,91 +16,76 @@ import { v4 as uuidv4 } from 'uuid'; import GitlabMember from '../shared/types/Gitlab/GitlabMember'; import GitlabAccessLevel from '../shared/types/Gitlab/GitlabAccessLevel'; import { Prisma } from '@prisma/client'; -import { Enonce, Exercice } from '../types/DatabaseTypes'; +import { Assignment, Exercise } from '../types/DatabaseTypes'; import db from '../helpers/DatabaseHelper'; import SecurityCheckType from '../types/SecurityCheckType'; import GitlabTreeFile from '../shared/types/Gitlab/GitlabTreeFile'; import GitlabFile from '../shared/types/Gitlab/GitlabFile'; -import EnonceFile from '../shared/types/Dojo/EnonceFile'; import GitlabTreeFileType from '../shared/types/Gitlab/GitlabTreeFileType'; import JSON5 from 'json5'; -import ExerciceResultsFile from '../shared/types/Dojo/ExerciceResultsFile'; import fs from 'fs'; import path from 'path'; +import AssignmentFile from '../shared/types/Dojo/AssignmentFile'; +import ExerciseResultsFile from '../shared/types/Dojo/ExerciseResultsFile'; -class ExerciceRoutes implements RoutesManager { - private readonly exerciceValidator: ExpressValidator.Schema = { +class ExerciseRoutes implements RoutesManager { + private readonly exerciseValidator: ExpressValidator.Schema = { members: { - trim : true, - notEmpty : true, - customSanitizer: DojoValidators.jsonSanitizer + trim: true, notEmpty: true, customSanitizer: DojoValidators.jsonSanitizer } }; private readonly resultValidator: ExpressValidator.Schema = { - exitCode : { - isInt: true, - toInt: true - }, - commit : { - trim : true, - notEmpty : true, - customSanitizer: DojoValidators.jsonSanitizer - }, - results : { - trim : true, - notEmpty : true, - custom : DojoValidators.exerciceResultsValidator, - customSanitizer: DojoValidators.jsonSanitizer - }, - files : { - trim : true, - notEmpty : true, - customSanitizer: DojoValidators.jsonSanitizer - }, - archiveBase64: { - isBase64: true, - notEmpty: true + exitCode : { + isInt: true, toInt: true + }, commit : { + trim: true, notEmpty: true, customSanitizer: DojoValidators.jsonSanitizer + }, results : { + trim: true, notEmpty: true, custom: DojoValidators.exerciseResultsValidator, customSanitizer: DojoValidators.jsonSanitizer + }, files : { + trim: true, notEmpty: true, customSanitizer: DojoValidators.jsonSanitizer + }, archiveBase64: { + isBase64: true, notEmpty: true } }; registerOnBackend(backend: Express) { - backend.post('/enonces/:enonceNameOrUrl/exercices', SecurityMiddleware.check(true, SecurityCheckType.ENONCE_IS_PUBLISHED), ParamsValidatorMiddleware.validate(this.exerciceValidator), this.createExercice.bind(this)); + backend.post('/assignments/:assignmentNameOrUrl/exercises', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_IS_PUBLISHED), ParamsValidatorMiddleware.validate(this.exerciseValidator), this.createExercise.bind(this)); - backend.get('/exercices/:exerciceId/enonce', SecurityMiddleware.check(false, SecurityCheckType.EXERCICE_SECRET), this.getEnonce.bind(this)); + backend.get('/exercises/:exerciseId/assignment', SecurityMiddleware.check(false, SecurityCheckType.EXERCISE_SECRET), this.getAssignment.bind(this)); - backend.post('/exercices/:exerciceId/results', SecurityMiddleware.check(false, SecurityCheckType.EXERCICE_SECRET), ParamsValidatorMiddleware.validate(this.resultValidator), this.createResult.bind(this)); + backend.post('/exercises/:exerciseId/results', SecurityMiddleware.check(false, SecurityCheckType.EXERCISE_SECRET), ParamsValidatorMiddleware.validate(this.resultValidator), this.createResult.bind(this)); } - private getExerciceName(enonce: Enonce, members: Array<GitlabUser>, suffix: number): string { - return `DojoEx - ${ enonce.name } - ${ members.map(member => member.username).join(' + ') }${ suffix > 0 ? ` - ${ suffix }` : '' }`; + private getExerciseName(assignment: Assignment, members: Array<GitlabUser>, suffix: number): string { + return `DojoEx - ${ assignment.name } - ${ members.map(member => member.username).join(' + ') }${ suffix > 0 ? ` - ${ suffix }` : '' }`; } - private getExercicePath(enonce: Enonce, exerciceId: string): string { - return `dojo-ex_${ (enonce.gitlabLastInfo as unknown as GitlabRepository).path }_${ exerciceId }`; + private getExercisePath(assignment: Assignment, exerciseId: string): string { + return `dojo-ex_${ (assignment.gitlabLastInfo as unknown as GitlabRepository).path }_${ exerciseId }`; } - private async createExercice(req: express.Request, res: express.Response) { + private async createExercise(req: express.Request, res: express.Response) { const params: { members: Array<GitlabUser> } = req.body; params.members = [ await req.session.profile.gitlabProfile!.value, ...params.members ].removeObjectDuplicates(gitlabUser => gitlabUser.id); - const enonce: Enonce = req.boundParams.enonce!; + const assignment: Assignment = req.boundParams.assignment!; - const exerciceId: string = uuidv4(); + const exerciseId: string = uuidv4(); const secret: string = uuidv4(); let repository!: GitlabRepository; let suffix: number = 0; do { try { - repository = await GitlabManager.forkRepository((enonce.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciceName(enonce, params.members, suffix), this.getExercicePath(req.boundParams.enonce!, exerciceId), Config.exercice.default.description.replace('{{ENONCE_NAME}}', enonce.name), Config.exercice.default.visibility, Config.gitlab.group.exercices); + repository = await GitlabManager.forkRepository((assignment.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciseName(assignment, params.members, suffix), this.getExercisePath(req.boundParams.assignment!, exerciseId), Config.exercise.default.description.replace('{{ASSIGNMENT_NAME}}', assignment.name), Config.exercise.default.visibility, Config.gitlab.group.exercises); await GitlabManager.protectBranch(repository.id, '*', false, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER); - await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCICE_ID', exerciceId, false, true); + await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_EXERCISE_ID', exerciseId, false, true); await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_SECRET', secret, false, true); - await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_RESULTS_FOLDER', Config.exercice.pipelineResultsFolder, false, false); + await GitlabManager.addRepositoryVariable(repository.id, 'DOJO_RESULTS_FOLDER', Config.exercise.pipelineResultsFolder, false, false); break; } catch ( error ) { @@ -114,14 +99,14 @@ class ExerciceRoutes implements RoutesManager { return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(); } } - } while ( suffix < Config.exercice.maxSameName ); + } while ( suffix < Config.exercise.maxSameName ); - if ( suffix >= Config.exercice.maxSameName ) { + if ( suffix >= Config.exercise.maxSameName ) { return res.status(StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE).send(); } try { - await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/exercice_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)'); + await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/exercise_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)'); } catch ( error ) { logger.error(error); @@ -133,7 +118,7 @@ class ExerciceRoutes implements RoutesManager { } try { - await Promise.all([ ...new Set([ ...enonce.staff.map(user => user.gitlabId), ...params.members.map(member => member.id) ]) ].map(async (memberId: number): Promise<GitlabMember | false> => { + await Promise.all([ ...new Set([ ...assignment.staff.map(user => user.gitlabId), ...params.members.map(member => member.id) ]) ].map(async (memberId: number): Promise<GitlabMember | false> => { try { return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER); } catch ( e ) { @@ -141,37 +126,26 @@ class ExerciceRoutes implements RoutesManager { } })); - const exercice: Exercice = await db.exercice.create({ + const exercise: Exercise = await db.exercise.create({ data: { - id : exerciceId, - enonceName : enonce.name, - name : repository.name, - secret : secret, - gitlabId : repository.id, - gitlabLink : repository.web_url, - gitlabCreationInfo: repository as unknown as Prisma.JsonObject, - gitlabLastInfo : repository as unknown as Prisma.JsonObject, - gitlabLastInfoDate: new Date(), - members : { + id: exerciseId, assignmentName: assignment.name, name: repository.name, secret: secret, gitlabId: repository.id, gitlabLink: repository.web_url, gitlabCreationInfo: repository as unknown as Prisma.JsonObject, gitlabLastInfo: repository as unknown as Prisma.JsonObject, gitlabLastInfoDate: new Date(), members: { connectOrCreate: [ ...params.members.map(gitlabUser => { return { - create: { - gitlabId : gitlabUser.id, - firstname: gitlabUser.name - }, - where : { + create : { + gitlabId: gitlabUser.id, firstname: gitlabUser.name + }, where: { gitlabId: gitlabUser.id } }; }) ] } } - }) as unknown as Exercice; + }) as unknown as Exercise; - return req.session.sendResponse(res, StatusCodes.OK, exercice); + return req.session.sendResponse(res, StatusCodes.OK, exercise); } catch ( error ) { logger.error(error); - + if ( error instanceof AxiosError ) { return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send(); } @@ -180,29 +154,29 @@ class ExerciceRoutes implements RoutesManager { } } - private async getEnonce(req: express.Request, res: express.Response) { - const repoTree: Array<GitlabTreeFile> = await GitlabManager.getRepositoryTree(req.boundParams.exercice!.enonce.gitlabId); + private async getAssignment(req: express.Request, res: express.Response) { + const repoTree: Array<GitlabTreeFile> = await GitlabManager.getRepositoryTree(req.boundParams.exercise!.assignment.gitlabId); - let enonceHjsonFile!: GitlabFile; - let immutableFiles: Array<GitlabFile> = await Promise.all(Config.enonce.baseFiles.map(async (baseFile: string) => { - let file = await GitlabManager.getFile(req.boundParams.exercice!.enonce.gitlabId, baseFile); + let assignmentHjsonFile!: GitlabFile; + let immutableFiles: Array<GitlabFile> = await Promise.all(Config.assignment.baseFiles.map(async (baseFile: string) => { + let file = await GitlabManager.getFile(req.boundParams.exercise!.assignment.gitlabId, baseFile); - if ( baseFile === Config.enonce.filename ) { - enonceHjsonFile = file; + if ( baseFile === Config.assignment.filename ) { + assignmentHjsonFile = file; } return file; })); - const dojoEnonceFile: EnonceFile = JSON5.parse(atob(enonceHjsonFile.content)) as EnonceFile; + const dojoAssignmentFile: AssignmentFile = JSON5.parse(atob(assignmentHjsonFile.content)) as AssignmentFile; - const immutablePaths = dojoEnonceFile.immutable.map(fileDescriptor => fileDescriptor.path); + const immutablePaths = dojoAssignmentFile.immutable.map(fileDescriptor => fileDescriptor.path); await Promise.all(repoTree.map(async gitlabTreeFile => { if ( gitlabTreeFile.type == GitlabTreeFileType.BLOB ) { for ( const immutablePath of immutablePaths ) { if ( gitlabTreeFile.path.startsWith(immutablePath) ) { - immutableFiles.push(await GitlabManager.getFile(req.boundParams.exercice!.enonce.gitlabId, gitlabTreeFile.path)); + immutableFiles.push(await GitlabManager.getFile(req.boundParams.exercise!.assignment.gitlabId, gitlabTreeFile.path)); break; } } @@ -210,32 +184,25 @@ class ExerciceRoutes implements RoutesManager { })); return req.session.sendResponse(res, StatusCodes.OK, { - enonce : (req.boundParams.exercice as Exercice).enonce, - enonceFile: dojoEnonceFile, - immutable : immutableFiles + assignment: (req.boundParams.exercise as Exercise).assignment, assignmentFile: dojoAssignmentFile, immutable: immutableFiles }); } private async createResult(req: express.Request, res: express.Response) { - const params: { exitCode: number, commit: any, results: ExerciceResultsFile, files: any, archiveBase64: string } = req.body; - const exercice: Exercice = req.boundParams.exercice!; + const params: { exitCode: number, commit: any, results: ExerciseResultsFile, files: any, archiveBase64: string } = req.body; + const exercise: Exercise = req.boundParams.exercise!; const result = await db.result.create({ data: { - exerciceId: exercice.id, - exitCode : params.exitCode, - success : params.results.success, - commit : params.commit, - results : params.results as unknown as Prisma.JsonObject, - files : params.files + exerciseId: exercise.id, exitCode: params.exitCode, success: params.results.success, commit: params.commit, results: params.results as unknown as Prisma.JsonObject, files: params.files } }); - fs.writeFileSync(path.join(Config.getResultsFolder(exercice), `${ result.dateTime.toISOString().replace(/:/g, '_') }.tar.gz`), params.archiveBase64, 'base64'); + fs.writeFileSync(path.join(Config.getResultsFolder(exercise), `${ result.dateTime.toISOString().replace(/:/g, '_') }.tar.gz`), params.archiveBase64, 'base64'); req.session.sendResponse(res, StatusCodes.OK); } } -export default new ExerciceRoutes(); +export default new ExerciseRoutes(); diff --git a/ExpressAPI/src/routes/GitlabRoutes.ts b/ExpressAPI/src/routes/GitlabRoutes.ts index ad03cfc..40ad2d9 100644 --- a/ExpressAPI/src/routes/GitlabRoutes.ts +++ b/ExpressAPI/src/routes/GitlabRoutes.ts @@ -6,7 +6,7 @@ import SecurityCheckType from '../types/SecurityCheckType'; import GitlabManager from '../managers/GitlabManager'; -class EnonceRoutes implements RoutesManager { +class GitlabRoutes implements RoutesManager { registerOnBackend(backend: Express) { backend.get('/gitlab/project/:idOrNamespace/checkTemplateAccess', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), this.checkTemplateAccess); } @@ -19,4 +19,4 @@ class EnonceRoutes implements RoutesManager { } -export default new EnonceRoutes(); +export default new GitlabRoutes(); diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared index f33e4e0..4cbcc73 160000 --- a/ExpressAPI/src/shared +++ b/ExpressAPI/src/shared @@ -1 +1 @@ -Subproject commit f33e4e0c7b34f9060e8995550920d25cd3e73c40 +Subproject commit 4cbcc7398459d7a2de027292fa0522c7fa592935 diff --git a/ExpressAPI/src/types/DatabaseTypes.ts b/ExpressAPI/src/types/DatabaseTypes.ts index 4627dcb..bdb25f8 100644 --- a/ExpressAPI/src/types/DatabaseTypes.ts +++ b/ExpressAPI/src/types/DatabaseTypes.ts @@ -3,27 +3,27 @@ import LazyVal from '../shared/helpers/LazyVal'; import GitlabUser from '../shared/types/Gitlab/GitlabUser'; -const userBase = Prisma.validator<Prisma.UserArgs>()({ - include: { exercices: true } - }); -const enonceBase = Prisma.validator<Prisma.EnonceArgs>()({ - include: { - exercices: true, - staff : true - } - }); -const exerciceBase = Prisma.validator<Prisma.ExerciceArgs>()({ - include: { - enonce : true, - members: true, - results: true - } - }); -const resultBase = Prisma.validator<Prisma.ResultArgs>()({ - include: { - exercice: true - } - }); +const userBase = Prisma.validator<Prisma.UserDefaultArgs>()({ + include: { exercises: true } + }); +const assignmentBase = Prisma.validator<Prisma.AssignmentDefaultArgs>()({ + include: { + exercises: true, + staff : true + } + }); +const exerciseBase = Prisma.validator<Prisma.ExerciseDefaultArgs>()({ + include: { + assignment: true, + members : true, + results : true + } + }); +const resultBase = Prisma.validator<Prisma.ResultDefaultArgs>()({ + include: { + exercise: true + } + }); export type User = Omit<Prisma.UserGetPayload<typeof userBase>, 'password'> & { @@ -31,6 +31,6 @@ export type User = Omit<Prisma.UserGetPayload<typeof userBase>, 'password'> & { isTeachingStaff: boolean gitlabProfile: LazyVal<GitlabUser> } -export type Enonce = Prisma.EnonceGetPayload<typeof enonceBase> -export type Exercice = Prisma.ExerciceGetPayload<typeof exerciceBase> +export type Assignment = Prisma.AssignmentGetPayload<typeof assignmentBase> +export type Exercise = Prisma.ExerciseGetPayload<typeof exerciseBase> export type Result = Prisma.ResultGetPayload<typeof resultBase> \ No newline at end of file diff --git a/ExpressAPI/src/types/SecurityCheckType.ts b/ExpressAPI/src/types/SecurityCheckType.ts index b063ff9..3a0b733 100644 --- a/ExpressAPI/src/types/SecurityCheckType.ts +++ b/ExpressAPI/src/types/SecurityCheckType.ts @@ -1,8 +1,8 @@ enum SecurityCheckType { - TEACHING_STAFF = 'teachingStaff', - ENONCE_STAFF = 'enonceStaff', - ENONCE_IS_PUBLISHED = 'enonceIsPublished', - EXERCICE_SECRET = 'exerciceSecret', + TEACHING_STAFF = 'teachingStaff', + ASSIGNMENT_STAFF = 'assignmentStaff', + ASSIGNMENT_IS_PUBLISHED = 'assignmentIsPublished', + EXERCISE_SECRET = 'exerciseSecret', } diff --git a/ExpressAPI/src/types/express/index.d.ts b/ExpressAPI/src/types/express/index.d.ts index c33801d..e7ac814 100644 --- a/ExpressAPI/src/types/express/index.d.ts +++ b/ExpressAPI/src/types/express/index.d.ts @@ -1,5 +1,5 @@ -import Session from '../../controllers/Session'; -import { Enonce, Exercice } from '../DatabaseTypes'; +import Session from '../../controllers/Session'; +import { Assignment, Exercise } from '../DatabaseTypes'; // to make the file a module and avoid the TypeScript error export {}; @@ -9,7 +9,7 @@ declare global { export interface Request { session: Session, boundParams: { - enonce: Enonce | undefined, exercice: Exercice | undefined + assignment: Assignment | undefined, exercise: Exercise | undefined } } } -- GitLab