Skip to content
Snippets Groups Projects
Select Git revision
  • 1192955b36bb1bab9fa1325749f58c1a862d6bcd
  • main default protected
  • jw_sonar
  • v6.0.0 protected
  • bedran_exercise-list
  • ask-user-to-delete-exercises-on-duplicates
  • update-dependencies
  • jw_sonar_backup
  • add_route_assignments
  • 6.0.0-dev
  • 5.0.1
  • 5.0.0
  • 4.1.0
  • 4.0.0
  • 3.5.3
  • 3.5.2
  • 3.5.1
  • 3.5.0
  • 3.4.2
  • 3.4.1
  • 3.4.0
  • 3.3.0
  • 3.2.0
  • 3.1.3
  • 3.1.2
  • 3.1.1
  • 3.1.0
  • 3.0.1
  • 3.0.0
29 results

ExerciceRoutes.ts

Blame
  • ExerciceRoutes.ts 9.69 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 ApiRequest                from '../types/ApiRequest';
    import SecurityMiddleware        from '../middlewares/SecurityMiddleware';
    import GitlabUser                from '../shared/types/Gitlab/GitlabUser';
    import GitlabManager             from '../managers/GitlabManager';
    import Config                    from '../config/Config';
    import GitlabRepository          from '../shared/types/Gitlab/GitlabRepository';
    import { AxiosError }            from 'axios';
    import logger                    from '../shared/logging/WinstonLogger';
    import DojoValidators            from '../helpers/DojoValidators';
    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 db                        from '../helpers/DatabaseHelper';
    import SecurityCheckType         from '../types/SecurityCheckType';
    import GitlabTreeFile            from '../shared/types/Gitlab/GitlabTreeFile';
    import GitlabFile                from '../shared/types/Gitlab/GitlabFile';
    import YAML                      from 'yaml';
    import EnonceFile                from '../shared/types/Dojo/EnonceFile';
    import GitlabTreeFileType        from '../shared/types/Gitlab/GitlabTreeFileType';
    
    
    class ExerciceRoutes implements RoutesManager {
        private readonly exerciceValidator: ExpressValidator.Schema = {
            members: {
                trim           : true,
                notEmpty       : true,
                customSanitizer: DojoValidators.jsonSanitizer
            }
        };
    
        registerOnBackend(backend: Express) {
            backend.post('/enonces/:enonceNameOrUrl/exercices', SecurityMiddleware.check(true, SecurityCheckType.ENONCE_IS_PUBLISHED), ParamsValidatorMiddleware.validate(this.exerciceValidator), this.createExercice.bind(this));
    
            backend.get('/exercices/:exerciceId/enonce', SecurityMiddleware.check(false, SecurityCheckType.EXERCICE_SECRET), this.getEnonce.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 getExercicePath(enonce: Enonce, exerciceId: string): string {
            return `dojo-ex_${ (enonce.gitlabLastInfo as unknown as GitlabRepository).path }_${ exerciceId }`;
        }
    
        private async createExercice(req: ApiRequest, 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 exerciceId: 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);
    
                    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_SECRET', secret, false, true);
    
                    break;
                } catch ( error ) {
                    if ( error instanceof AxiosError ) {
                        if ( error.response.data.message.name && error.response.data.message.name == 'has already been taken' ) {
                            suffix++;
                        } else {
                            return res.status(error.response.status).send();
                        }
                    } else {
                        return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
                    }
                }
            } while ( suffix < Config.exercice.maxSameName );
    
            if ( suffix >= Config.exercice.maxSameName ) {
                return res.status(StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE).send();
            }
    
            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> => {
                    try {
                        return await GitlabManager.addRepositoryMember(repository.id, memberId, GitlabAccessLevel.DEVELOPER);
                    } catch ( e ) {
                        return false;
                    }
                }));
    
                const exercice: Exercice = await db.exercice.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           : {
                                                                                connectOrCreate: [ ...params.members.map(gitlabUser => {
                                                                                    return {
                                                                                        create: {
                                                                                            gitlabId : gitlabUser.id,
                                                                                            firstname: gitlabUser.name
                                                                                        },
                                                                                        where : {
                                                                                            gitlabId: gitlabUser.id
                                                                                        }
                                                                                    };
                                                                                }) ]
                                                                            }
                                                                        }
                                                                    });
    
                return req.session.sendResponse(res, StatusCodes.OK, exercice);
            } catch ( error ) {
                if ( error instanceof AxiosError ) {
                    return res.status(error.response.status).send();
                }
    
                logger.error(error);
                return res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
            }
        }
    
        private async getEnonce(req: ApiRequest, res: express.Response) {
            const repoTree: Array<GitlabTreeFile> = await GitlabManager.getRepositoryTree(req.boundParams.exercice.gitlabId);
    
            let enonceYamlFile: GitlabFile;
            let immutableFiles: Array<GitlabFile> = await Promise.all(Config.enonce.baseFiles.map(async (baseFile: string) => {
                let file = await GitlabManager.getFile(req.boundParams.exercice.gitlabId, baseFile);
    
                if ( baseFile === Config.enonce.filename ) {
                    enonceYamlFile = file;
                }
    
                return file;
            }));
    
            const dojoEnonceFile: EnonceFile = YAML.parse(atob(enonceYamlFile.content));
    
            const immutablePaths = dojoEnonceFile.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.gitlabId, gitlabTreeFile.path));
                            break;
                        }
                    }
                }
            }));
    
            return req.session.sendResponse(res, StatusCodes.OK, {
                enonce    : (req.boundParams.exercice as Exercice).enonce,
                enonceFile: dojoEnonceFile,
                immutable : immutableFiles
            });
        }
    }
    
    
    export default new ExerciceRoutes();