Select Git revision
AssignmentRoutes.ts
kelly.nguyen authored
AssignmentRoutes.ts 12.23 KiB
import { Express } from 'express-serve-static-core';
import express from 'express';
import * as ExpressValidator from 'express-validator';
import { StatusCodes } from 'http-status-codes';
import RoutesManager from '../express/RoutesManager';
import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware';
import SecurityMiddleware from '../middlewares/SecurityMiddleware';
import SecurityCheckType from '../types/SecurityCheckType';
import GitlabUser from '../shared/types/Gitlab/GitlabUser';
import GitlabManager from '../managers/GitlabManager';
import Config from '../config/Config';
import GitlabMember from '../shared/types/Gitlab/GitlabMember';
import GitlabAccessLevel from '../shared/types/Gitlab/GitlabAccessLevel';
import GitlabRepository from '../shared/types/Gitlab/GitlabRepository';
import { AxiosError, HttpStatusCode } from 'axios';
import logger from '../shared/logging/WinstonLogger';
import DojoValidators from '../helpers/DojoValidators';
import { Prisma } from '@prisma/client';
import db from '../helpers/DatabaseHelper';
import { Assignment } from '../types/DatabaseTypes';
import AssignmentManager from '../managers/AssignmentManager';
import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility';
import fs from 'fs';
import path from 'path';
import SharedAssignmentHelper from '../shared/helpers/Dojo/SharedAssignmentHelper';
import GlobalHelper from '../helpers/GlobalHelper';
import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode';
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('/assignments/:assignmentNameOrUrl', SecurityMiddleware.check(true), this.getAssignment.bind(this));
backend.post('/assignments', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.assignmentValidator), this.createAssignment.bind(this));
backend.patch('/assignments/:assignmentNameOrUrl/publish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.publishAssignment.bind(this));
backend.patch('/assignments/:assignmentNameOrUrl/unpublish', SecurityMiddleware.check(true, SecurityCheckType.ASSIGNMENT_STAFF), this.unpublishAssignment.bind(this));
// backend.get('/assignments/:onlyMine', SecurityMiddleware.check(false, SecurityCheckType.TEACHING_STAFF),this.getAssignmentById.bind(this));
backend.get('/assignmentss', SecurityMiddleware.check(false, SecurityCheckType.TEACHING_STAFF),this.getAssignmentById.bind(this));
}
// 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 ( assignment && !assignment.published && !await AssignmentManager.isUserAllowedToAccessAssignment(assignment, req.session.profile) ) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.gitlabId;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.gitlabLink;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.gitlabCreationInfo;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.gitlabLastInfo;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.gitlabLastInfoDate;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.staff;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete assignment.exercises;
}
return assignment ? req.session.sendResponse(res, StatusCodes.OK, assignment) : res.status(StatusCodes.NOT_FOUND).send();
}
private async createAssignment(req: express.Request, res: express.Response) {
const params: {
name: string, members: Array<GitlabUser>, template: string
} = req.body;
params.members = [ await req.session.profile.gitlabProfile.value, ...params.members ];
params.members = params.members.removeObjectDuplicates(gitlabUser => gitlabUser.id);
let repository: GitlabRepository;
try {
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);
} catch ( error ) {
logger.error('Repo creation error');
logger.error(error);
if ( error instanceof AxiosError ) {
if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) {
return res.status(StatusCodes.CONFLICT).send();
}
return res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
}
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
}
await new Promise(resolve => setTimeout(resolve, Config.gitlab.repository.timeoutAfterCreation));
try {
await GitlabManager.protectBranch(repository.id, '*', true, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.DEVELOPER, GitlabAccessLevel.OWNER);
await GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status');
} catch ( error ) {
return GlobalHelper.repositoryCreationError('Repo params error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository);
}
try {
await GitlabManager.deleteFile(repository.id, '.gitlab-ci.yml', 'Remove .gitlab-ci.yml');
} catch ( error ) { /* empty */ }
try {
await GitlabManager.createFile(repository.id, '.gitlab-ci.yml', fs.readFileSync(path.join(__dirname, '../../assets/assignment_gitlab_ci.yml'), 'base64'), 'Add .gitlab-ci.yml (DO NOT MODIFY THIS FILE)');
} catch ( error ) {
return GlobalHelper.repositoryCreationError('CI file error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository);
}
try {
await Promise.all(params.members.map(member => member.id).map(async (memberId: number): Promise<GitlabMember | false> => {
try {
return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER);
} catch ( error ) {
logger.error('Add member error');
logger.error(error);
return false;
}
}));
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: {
id : gitlabUser.id,
gitlabUsername: gitlabUser.name
},
where : {
id: gitlabUser.id
}
};
}) ]
}
}
}) as unknown as Assignment;
return req.session.sendResponse(res, StatusCodes.OK, assignment);
} catch ( error ) {
return GlobalHelper.repositoryCreationError('DB error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_GITLAB_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, repository);
}
}
private async publishAssignment(req: express.Request, res: express.Response) {
return this.changeAssignmentPublishedStatus(true)(req, res);
}
private async unpublishAssignment(req: express.Request, res: express.Response) {
return this.changeAssignmentPublishedStatus(false)(req, res);
}
private changeAssignmentPublishedStatus(publish: boolean): (req: express.Request, res: express.Response) => Promise<void> {
return async (req: express.Request, res: express.Response): Promise<void> => {
if ( publish ) {
const isPublishable = await SharedAssignmentHelper.isPublishable(req.boundParams.assignment!.gitlabId);
if ( !isPublishable.isPublishable ) {
return req.session.sendResponse(res, StatusCodes.BAD_REQUEST, { lastPipeline: isPublishable.lastPipeline }, isPublishable.status?.message, isPublishable.status?.code);
}
}
try {
await GitlabManager.changeRepositoryVisibility(req.boundParams.assignment!.gitlabId, publish ? GitlabVisibility.INTERNAL : GitlabVisibility.PRIVATE);
await db.assignment.update({
where: {
name: req.boundParams.assignment!.name
},
data : {
published: publish
}
});
req.session.sendResponse(res, StatusCodes.OK);
} catch ( error ) {
if ( error instanceof AxiosError ) {
res.status(error.response?.status ?? HttpStatusCode.InternalServerError).send();
return;
}
logger.error(error);
res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
}
};
}
private async getAssignmentById(req : express.Request, res : express.Response) {
// const onlyMine = req.boundParams.user?.id;
// const users = await db.assignment.findMany();
// console.log(onlyMine);
// const assign = await db.user.findUnique({
// where: {
// id: onlyMine,
// },
// include: {
// assignments: true,
// },
// });
const assign = await db.assignment.findMany();
return req.session.sendResponse(res, StatusCodes.OK, assign);
}
// private async deleteAssignment(req : express.Request, res : express.Response) {
// }
}
export default new AssignmentRoutes();