From 0cef1fa3dfa878998bf58e40ee5e188623d403b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Minelli?= <michael@minelli.me>
Date: Wed, 28 Feb 2024 17:53:24 +0100
Subject: [PATCH] Sonar => Resolve issues

---
 ExpressAPI/prisma/seed.ts                     |  2 +-
 ExpressAPI/src/app.ts                         |  4 +-
 ExpressAPI/src/config/Config.ts               |  2 +-
 ExpressAPI/src/controllers/Session.ts         | 25 ++++----
 ExpressAPI/src/express/API.ts                 |  6 +-
 ExpressAPI/src/helpers/DatabaseHelper.ts      |  4 +-
 .../src/helpers/DojoCliVersionHelper.ts       |  2 -
 ExpressAPI/src/helpers/DojoModelsHelper.ts    |  3 +-
 ExpressAPI/src/helpers/DojoValidators.ts      | 13 ++--
 ExpressAPI/src/helpers/GlobalHelper.ts        |  4 +-
 .../Extensions/AssignmentResultExtension.ts   |  4 +-
 .../Prisma/Extensions/UserResultExtension.ts  |  8 +--
 ExpressAPI/src/logging/MorganMiddleware.ts    |  6 +-
 ExpressAPI/src/managers/GitlabManager.ts      | 22 ++++---
 ExpressAPI/src/managers/HttpManager.ts        | 12 ++--
 .../src/middlewares/SecurityMiddleware.ts     |  6 +-
 ExpressAPI/src/process/ClusterManager.ts      |  5 +-
 ExpressAPI/src/routes/AssignmentRoutes.ts     |  9 ++-
 ExpressAPI/src/routes/ExerciseRoutes.ts       | 59 +++++++++++++------
 ExpressAPI/src/shared                         |  2 +-
 sonar-project.properties                      |  3 +-
 21 files changed, 107 insertions(+), 94 deletions(-)

diff --git a/ExpressAPI/prisma/seed.ts b/ExpressAPI/prisma/seed.ts
index 20249cf..fc8dce5 100644
--- a/ExpressAPI/prisma/seed.ts
+++ b/ExpressAPI/prisma/seed.ts
@@ -16,7 +16,7 @@ async function main() {
 
 main().then(async () => {
     await db.$disconnect();
-}).catch(async (e) => {
+}).catch(async e => {
     logger.error(e);
     await db.$disconnect();
     process.exit(1);
diff --git a/ExpressAPI/src/app.ts b/ExpressAPI/src/app.ts
index 9317f38..487035a 100644
--- a/ExpressAPI/src/app.ts
+++ b/ExpressAPI/src/app.ts
@@ -13,7 +13,5 @@ HttpManager.registerAxiosInterceptor();
     role         : WorkerRole.API,
     quantity     : ClusterManager.CORES,
     restartOnFail: true,
-    loadTask     : () => {
-        return new API();
-    }
+    loadTask     : () => new API()
 } ])).run();
diff --git a/ExpressAPI/src/config/Config.ts b/ExpressAPI/src/config/Config.ts
index f5a736c..a768fde 100644
--- a/ExpressAPI/src/config/Config.ts
+++ b/ExpressAPI/src/config/Config.ts
@@ -19,7 +19,7 @@ class Config {
         version: {
             [client: string]: string
         }
-    }; // { version: { CLIENT: CONDITION } }
+    };
 
     public readonly dojoCLI: {
         versionUpdatePeriodMs: number
diff --git a/ExpressAPI/src/controllers/Session.ts b/ExpressAPI/src/controllers/Session.ts
index 17efe68..7be2dea 100644
--- a/ExpressAPI/src/controllers/Session.ts
+++ b/ExpressAPI/src/controllers/Session.ts
@@ -19,30 +19,27 @@ class Session {
         this._profile = newProfile;
     }
 
-    constructor() { }
-
     async initSession(req: express.Request, res: express.Response) {
         const authorization = req.headers.authorization;
-        if ( authorization ) {
-            if ( authorization.startsWith('Bearer ') ) {
-                const jwtToken = authorization.replace('Bearer ', '');
+        if ( authorization && authorization.startsWith('Bearer ') ) {
+            const jwtToken = authorization.replace('Bearer ', '');
 
-                try {
-                    const jwtData = jwt.verify(jwtToken, Config.jwtConfig.secret) as JwtPayload;
+            try {
+                const jwtData = jwt.verify(jwtToken, Config.jwtConfig.secret) as JwtPayload;
 
-                    if ( jwtData.profile ) {
-                        this.profile = jwtData.profile;
-                        this.profile = await UserManager.getById(this.profile.id!) ?? this.profile;
-                    }
-                } catch ( err ) {
-                    res.sendStatus(StatusCodes.UNAUTHORIZED).end();
+                if ( jwtData.profile ) {
+                    this.profile = jwtData.profile;
+                    this.profile = await UserManager.getById(this.profile.id!) ?? this.profile;
                 }
+            } catch ( err ) {
+                res.sendStatus(StatusCodes.UNAUTHORIZED).end();
             }
         }
     }
 
     private static getToken(profileJson: unknown): string | null {
-        return profileJson === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {});
+        const options = Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {};
+        return profileJson === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, options);
     }
 
     private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoBackendResponse<T>> {
diff --git a/ExpressAPI/src/express/API.ts b/ExpressAPI/src/express/API.ts
index 0d61b88..a55896a 100644
--- a/ExpressAPI/src/express/API.ts
+++ b/ExpressAPI/src/express/API.ts
@@ -45,7 +45,7 @@ class API implements WorkerTask {
         this.backend.use(cors()); //Allow CORS requests
         this.backend.use(compression()); //Compress responses
 
-        this.backend.use(async (req, res, next) => {
+        this.backend.use(async (_req, res, next) => {
             res.header('dojocli-latest-version', await DojoCliVersionHelper.getLatestVersion());
             next();
         });
@@ -59,9 +59,9 @@ class API implements WorkerTask {
                 url: '../OpenAPI.yaml'
             }
         };
-        this.backend.get('/docs/OpenAPI.yaml', (req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/OpenAPI.yaml')));
+        this.backend.get('/docs/OpenAPI.yaml', (_req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/OpenAPI.yaml')));
         this.backend.use('/docs/swagger', swaggerUi.serveFiles(undefined, options), swaggerUi.setup(undefined, options));
-        this.backend.get('/docs/redoc.html', (req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/redoc.html')));
+        this.backend.get('/docs/redoc.html', (_req, res) => res.sendFile(path.resolve(__dirname + '/../../assets/OpenAPI/redoc.html')));
 
         this.backend.get('/docs/', (req, res) => {
             const prefix = req.url.slice(-1) === '/' ? '' : 'docs/';
diff --git a/ExpressAPI/src/helpers/DatabaseHelper.ts b/ExpressAPI/src/helpers/DatabaseHelper.ts
index 9dc14bb..797615f 100644
--- a/ExpressAPI/src/helpers/DatabaseHelper.ts
+++ b/ExpressAPI/src/helpers/DatabaseHelper.ts
@@ -31,7 +31,7 @@ prisma.$on('warn', e => logger.warn(`Prisma => ${ e.message }`));
 prisma.$on('error', e => logger.error(`Prisma => ${ e.message }`));
 
 
-const db = prisma.$extends(UserQueryExtension).$extends(UserResultExtension).$extends(AssignmentResultExtension).$extends(ExerciseResultExtension);
+const DatabaseHelper = prisma.$extends(UserQueryExtension).$extends(UserResultExtension).$extends(AssignmentResultExtension).$extends(ExerciseResultExtension);
 
 
-export default db;
\ No newline at end of file
+export default DatabaseHelper;
\ No newline at end of file
diff --git a/ExpressAPI/src/helpers/DojoCliVersionHelper.ts b/ExpressAPI/src/helpers/DojoCliVersionHelper.ts
index a44e1ec..962bf1c 100644
--- a/ExpressAPI/src/helpers/DojoCliVersionHelper.ts
+++ b/ExpressAPI/src/helpers/DojoCliVersionHelper.ts
@@ -7,8 +7,6 @@ class DojoCliVersionHelper {
     private latestUpdate: Date | undefined;
     private latestVersion: string | undefined;
 
-    constructor() { }
-
     private async updateVersion(): Promise<void> {
         const releases: Array<GitlabRelease> = await GitlabManager.getRepositoryReleases(Config.dojoCLI.repositoryId);
         for ( const release of releases ) {
diff --git a/ExpressAPI/src/helpers/DojoModelsHelper.ts b/ExpressAPI/src/helpers/DojoModelsHelper.ts
index 8fa9e5f..3b3c35c 100644
--- a/ExpressAPI/src/helpers/DojoModelsHelper.ts
+++ b/ExpressAPI/src/helpers/DojoModelsHelper.ts
@@ -9,8 +9,7 @@ class DojoModelsHelper {
      * @param depth The depth of the search for LazyVal instances
      */
     async getFullSerializableObject<T extends NonNullable<unknown>>(obj: T, depth: number = 0): Promise<unknown> {
-        /* eslint-disable-next-line  @typescript-eslint/no-explicit-any */
-        const result: any = {};
+        const result: { [key: string]: unknown } = {};
 
         for ( const key in obj ) {
             let value: unknown = obj[key];
diff --git a/ExpressAPI/src/helpers/DojoValidators.ts b/ExpressAPI/src/helpers/DojoValidators.ts
index 3846541..2f3797f 100644
--- a/ExpressAPI/src/helpers/DojoValidators.ts
+++ b/ExpressAPI/src/helpers/DojoValidators.ts
@@ -31,9 +31,9 @@ class DojoValidators {
     }
 
     readonly nullSanitizer = this.toValidatorSchemaOptions({
-                                                               options: (value) => {
+                                                               options: value => {
                                                                    try {
-                                                                       return value == 'null' || value == 'undefined' || value == '' ? null : value;
+                                                                       return value === 'null' || value === 'undefined' || value === '' ? null : value;
                                                                    } catch ( error ) {
                                                                        logger.error(`null sanitizer error: ${ error }`);
 
@@ -43,7 +43,7 @@ class DojoValidators {
                                                            });
 
     readonly jsonSanitizer = this.toValidatorSchemaOptions({
-                                                               options: (value) => {
+                                                               options: value => {
                                                                    try {
                                                                        return JSON.parse(value as string);
                                                                    } catch ( e ) {
@@ -62,7 +62,7 @@ class DojoValidators {
                                                                           return new Promise((resolve, reject) => {
                                                                               const template = this.getParamValue(req, path) as string;
                                                                               if ( template ) {
-                                                                                  GitlabManager.checkTemplateAccess(template, req).then((templateAccess) => {
+                                                                                  GitlabManager.checkTemplateAccess(template, req).then(templateAccess => {
                                                                                       templateAccess !== StatusCodes.OK ? reject() : resolve(true);
                                                                                   });
                                                                               }
@@ -79,7 +79,8 @@ class DojoValidators {
                                                                           try {
                                                                               const template = this.getParamValue(req, path);
                                                                               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`;
+                                                                                  const gitlabUrlWithCredentials = Config.gitlab.urls[0].replace(/^([a-z]{3,5}:\/{2})?(.*)/, `$1${ Config.gitlab.account.username }:${ Config.gitlab.account.token }@$2`);
+                                                                                  return `${ gitlabUrlWithCredentials }${ template }.git`;
                                                                               } else {
                                                                                   return Config.assignment.default.template;
                                                                               }
@@ -121,7 +122,7 @@ class DojoValidators {
                                                                                   if ( exerciseIdOrUrl ) {
                                                                                       ParamsCallbackManager.initBoundParams(req);
 
-                                                                                      ExerciseManager.get(exerciseIdOrUrl).then((exercise) => {
+                                                                                      ExerciseManager.get(exerciseIdOrUrl).then(exercise => {
                                                                                           req.boundParams.exercise = exercise;
 
                                                                                           exercise !== undefined ? resolve(true) : reject();
diff --git a/ExpressAPI/src/helpers/GlobalHelper.ts b/ExpressAPI/src/helpers/GlobalHelper.ts
index 8a425a0..e7e6e34 100644
--- a/ExpressAPI/src/helpers/GlobalHelper.ts
+++ b/ExpressAPI/src/helpers/GlobalHelper.ts
@@ -16,9 +16,9 @@ class GlobalHelper {
             if ( repositoryToRemove ) {
                 await GitlabManager.deleteRepository(repositoryToRemove.id);
             }
-        } catch ( error ) {
+        } catch ( deleteError ) {
             logger.error('Repository deletion error');
-            logger.error(error);
+            logger.error(deleteError);
         }
 
         if ( error instanceof AxiosError ) {
diff --git a/ExpressAPI/src/helpers/Prisma/Extensions/AssignmentResultExtension.ts b/ExpressAPI/src/helpers/Prisma/Extensions/AssignmentResultExtension.ts
index a5f8139..4c1d8be 100644
--- a/ExpressAPI/src/helpers/Prisma/Extensions/AssignmentResultExtension.ts
+++ b/ExpressAPI/src/helpers/Prisma/Extensions/AssignmentResultExtension.ts
@@ -30,9 +30,7 @@ export default Prisma.defineExtension(client => {
                                    assignment: {
                                        corrections: {
                                            compute(assignment) {
-                                               return new LazyVal<Array<Partial<Exercise>> | undefined>(() => {
-                                                   return getCorrections(assignment);
-                                               });
+                                               return new LazyVal<Array<Partial<Exercise>> | undefined>(() => getCorrections(assignment));
                                            }
                                        }
                                    }
diff --git a/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts b/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts
index 9ece43d..8a8640e 100644
--- a/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts
+++ b/ExpressAPI/src/helpers/Prisma/Extensions/UserResultExtension.ts
@@ -13,7 +13,7 @@ export default Prisma.defineExtension(client => {
                                                role: true
                                            },
                                            compute(user) {
-                                               return user.role == UserRole.TEACHING_STAFF || user.role == UserRole.ADMIN;
+                                               return user.role === UserRole.TEACHING_STAFF || user.role === UserRole.ADMIN;
                                            }
                                        },
                                        isAdmin        : {
@@ -21,14 +21,12 @@ export default Prisma.defineExtension(client => {
                                                role: true
                                            },
                                            compute(user) {
-                                               return user.role == UserRole.ADMIN;
+                                               return user.role === UserRole.ADMIN;
                                            }
                                        },
                                        gitlabProfile  : {
                                            compute(user) {
-                                               return new LazyVal<GitlabUser | undefined>(() => {
-                                                   return GitlabManager.getUserById(user.id);
-                                               });
+                                               return new LazyVal<GitlabUser | undefined>(() => GitlabManager.getUserById(user.id));
                                            }
                                        }
                                    }
diff --git a/ExpressAPI/src/logging/MorganMiddleware.ts b/ExpressAPI/src/logging/MorganMiddleware.ts
index d1a1f72..6da9b45 100644
--- a/ExpressAPI/src/logging/MorganMiddleware.ts
+++ b/ExpressAPI/src/logging/MorganMiddleware.ts
@@ -3,12 +3,10 @@ import logger                    from '../shared/logging/WinstonLogger';
 
 
 const stream: StreamOptions = {
-    write: (message) => logger.http(message)
+    write: message => logger.http(message)
 };
 
-const skip = () => {
-    return false; //SharedConfig.production; 
-};
+const skip = () => false;
 
 const morganMiddleware = morgan(':method :url :status :res[content-length] - :response-time ms', {
     stream,
diff --git a/ExpressAPI/src/managers/GitlabManager.ts b/ExpressAPI/src/managers/GitlabManager.ts
index 3455f8b..ceadae5 100644
--- a/ExpressAPI/src/managers/GitlabManager.ts
+++ b/ExpressAPI/src/managers/GitlabManager.ts
@@ -97,11 +97,11 @@ class GitlabManager {
         }
     }
 
-    async createRepository(name: string, description: string, visibility: string, initializeWithReadme: boolean, namespace: number, sharedRunnersEnabled: boolean, wikiEnabled: boolean, import_url: string): Promise<GitlabRepository> {
+    async createRepository(name: string, description: string, visibility: string, initializeWithReadme: boolean, namespace: number, sharedRunnersEnabled: boolean, wikiEnabled: boolean, importUrl: string): Promise<GitlabRepository> {
         const response = await axios.post<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_CREATE), {
             name                  : name,
             description           : description,
-            import_url            : import_url,
+            import_url            : importUrl,
             initialize_with_readme: initializeWithReadme,
             namespace_id          : namespace,
             shared_runners_enabled: sharedRunnersEnabled,
@@ -112,8 +112,8 @@ class GitlabManager {
         return response.data;
     }
 
-    async deleteRepository(repoId: number): Promise<void> {
-        return await axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_DELETE).replace('{{id}}', String(repoId)));
+    deleteRepository(repoId: number): Promise<void> {
+        return axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_DELETE).replace('{{id}}', String(repoId)));
     }
 
     async forkRepository(forkId: number, name: string, path: string, description: string, visibility: string, namespace: number): Promise<GitlabRepository> {
@@ -134,8 +134,8 @@ class GitlabManager {
         return response.data;
     }
 
-    async changeRepositoryVisibility(repoId: number, visibility: GitlabVisibility): Promise<GitlabRepository> {
-        return await this.editRepository(repoId, { visibility: visibility.toString() });
+    changeRepositoryVisibility(repoId: number, visibility: GitlabVisibility): Promise<GitlabRepository> {
+        return this.editRepository(repoId, { visibility: visibility.toString() });
     }
 
     async addRepositoryMember(repoId: number, userId: number, accessLevel: GitlabAccessLevel): Promise<GitlabMember> {
@@ -240,8 +240,12 @@ class GitlabManager {
         return results;
     }
 
+    private getRepositoryFileUrl(repoId: number, filePath: string): string {
+        return this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath));
+    }
+
     async getFile(repoId: number, filePath: string, branch: string = 'main'): Promise<GitlabFile> {
-        const response = await axios.get<GitlabFile>(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
+        const response = await axios.get<GitlabFile>(this.getRepositoryFileUrl(repoId, filePath), {
             params: {
                 ref: branch
             }
@@ -253,7 +257,7 @@ class GitlabManager {
     private async createUpdateFile(create: boolean, repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
         const axiosFunction = create ? axios.post : axios.put;
 
-        await axiosFunction(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
+        await axiosFunction(this.getRepositoryFileUrl(repoId, filePath), {
             encoding      : 'base64',
             branch        : branch,
             commit_message: commitMessage,
@@ -272,7 +276,7 @@ class GitlabManager {
     }
 
     async deleteFile(repoId: number, filePath: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
-        await axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
+        await axios.delete(this.getRepositoryFileUrl(repoId, filePath), {
             data: {
                 branch        : branch,
                 commit_message: commitMessage,
diff --git a/ExpressAPI/src/managers/HttpManager.ts b/ExpressAPI/src/managers/HttpManager.ts
index 075e727..e67828d 100644
--- a/ExpressAPI/src/managers/HttpManager.ts
+++ b/ExpressAPI/src/managers/HttpManager.ts
@@ -12,15 +12,13 @@ class HttpManager {
     }
 
     private registerRequestInterceptor() {
-        axios.interceptors.request.use((config) => {
+        axios.interceptors.request.use(config => {
             if ( config.data instanceof FormData ) {
                 config.headers = { ...config.headers, ...(config.data as FormData).getHeaders() } as AxiosRequestHeaders;
             }
 
-            if ( config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 ) {
-                if ( !config.headers.DojoOverrideAuthorization ) {
-                    config.headers['PRIVATE-TOKEN'] = Config.gitlab.account.token;
-                }
+            if ( config.url && config.url.indexOf(SharedConfig.gitlab.apiURL) !== -1 && !config.headers.DojoOverrideAuthorization ) {
+                config.headers['PRIVATE-TOKEN'] = Config.gitlab.account.token;
             }
 
             if ( config.headers.DojoOverrideAuthorization && 'DojoAuthorizationHeader' in config.headers && 'DojoAuthorizationValue' in config.headers ) {
@@ -36,9 +34,7 @@ class HttpManager {
     }
 
     private registerResponseInterceptor() {
-        axios.interceptors.response.use((response) => {
-            return response;
-        }, (error) => {
+        axios.interceptors.response.use(response => response, error => {
             if ( error instanceof AxiosError ) {
                 logger.error(`${ JSON.stringify(error.response?.data) }`);
             } else {
diff --git a/ExpressAPI/src/middlewares/SecurityMiddleware.ts b/ExpressAPI/src/middlewares/SecurityMiddleware.ts
index 37e6e4a..6b4a075 100644
--- a/ExpressAPI/src/middlewares/SecurityMiddleware.ts
+++ b/ExpressAPI/src/middlewares/SecurityMiddleware.ts
@@ -9,10 +9,8 @@ class SecurityMiddleware {
     // First check if connected then check if at least ONE rule match. It's NOT an AND but it's a OR function.
     check(checkIfConnected: boolean, ...checkTypes: Array<SecurityCheckType>): (req: express.Request, res: express.Response, next: express.NextFunction) => void {
         return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
-            if ( checkIfConnected ) {
-                if ( req.session.profile === null || req.session.profile === undefined ) {
-                    return req.session.sendResponse(res, StatusCodes.UNAUTHORIZED);
-                }
+            if ( checkIfConnected && (req.session.profile === null || req.session.profile === undefined) ) {
+                return req.session.sendResponse(res, StatusCodes.UNAUTHORIZED);
             }
 
             let isAllowed = checkTypes.length === 0;
diff --git a/ExpressAPI/src/process/ClusterManager.ts b/ExpressAPI/src/process/ClusterManager.ts
index 945f755..fd061eb 100644
--- a/ExpressAPI/src/process/ClusterManager.ts
+++ b/ExpressAPI/src/process/ClusterManager.ts
@@ -11,10 +11,13 @@ import logger              from '../shared/logging/WinstonLogger';
  */
 class ClusterManager {
     public static readonly CORES = os.cpus().length;
+    private readonly strategy: ClusterStrategy;
 
     private workers: { [pid: number]: WorkerRole; } = [];
 
-    constructor(private strategy: ClusterStrategy) {}
+    constructor(strategy: ClusterStrategy) {
+        this.strategy = strategy;
+    }
 
     private getWorkerPool(role: WorkerRole): WorkerPool | undefined {
         return this.strategy.find(elem => elem.role === role);
diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts
index a11d53c..5b607ab 100644
--- a/ExpressAPI/src/routes/AssignmentRoutes.ts
+++ b/ExpressAPI/src/routes/AssignmentRoutes.ts
@@ -98,7 +98,7 @@ class AssignmentRoutes implements RoutesManager {
             logger.error(error);
 
             if ( error instanceof AxiosError ) {
-                if ( error.response?.data.message.name && error.response.data.message.name == 'has already been taken' ) {
+                if ( error.response?.data.message.name && error.response.data.message.name === 'has already been taken' ) {
                     return res.status(StatusCodes.CONFLICT).send();
                 }
 
@@ -174,7 +174,8 @@ class AssignmentRoutes implements RoutesManager {
             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);
+                    req.session.sendResponse(res, StatusCodes.BAD_REQUEST, { lastPipeline: isPublishable.lastPipeline }, isPublishable.status?.message, isPublishable.status?.code);
+                    return;
                 }
             }
 
@@ -200,12 +201,14 @@ class AssignmentRoutes implements RoutesManager {
                 logger.error(error);
                 res.status(StatusCodes.INTERNAL_SERVER_ERROR).send();
             }
+
+            return;
         };
     }
 
     private linkUpdateAssignmentCorrection(isUpdate: boolean): (req: express.Request, res: express.Response) => Promise<void> {
         return async (req: express.Request, res: express.Response): Promise<void> => {
-            if ( req.boundParams.exercise?.assignmentName != req.boundParams.assignment?.name ) {
+            if ( req.boundParams.exercise?.assignmentName !== req.boundParams.assignment?.name ) {
                 return req.session.sendResponse(res, StatusCodes.BAD_REQUEST, undefined, 'The exercise does not belong to the assignment', DojoStatusCode.ASSIGNMENT_EXERCISE_NOT_RELATED);
             }
 
diff --git a/ExpressAPI/src/routes/ExerciseRoutes.ts b/ExpressAPI/src/routes/ExerciseRoutes.ts
index 1317f5b..ff7cad6 100644
--- a/ExpressAPI/src/routes/ExerciseRoutes.ts
+++ b/ExpressAPI/src/routes/ExerciseRoutes.ts
@@ -78,62 +78,83 @@ class ExerciseRoutes implements RoutesManager {
     }
 
     private getExerciseName(assignment: Assignment, members: Array<GitlabUser>, suffix: number): string {
-        return `DojoEx - ${ assignment.name } - ${ members.map(member => member.username).sort((a, b) => a.localeCompare(b)).join(' + ') }${ suffix > 0 ? ` - ${ suffix }` : '' }`;
+        const memberNames: string = members.map(member => member.username).sort((a, b) => a.localeCompare(b)).join(' + ');
+        const suffixString: string = suffix > 0 ? ` - ${ suffix }` : '';
+        return `DojoEx - ${ assignment.name } - ${ memberNames }${ suffixString }`;
     }
 
     private getExercisePath(assignment: Assignment, exerciseId: string): string {
         return `dojo-ex_${ (assignment.gitlabLastInfo as unknown as GitlabRepository).path }_${ exerciseId }`;
     }
 
-    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 assignment: Assignment = req.boundParams.assignment!;
-
-
+    private async checkExerciseLimit(assignment: Assignment, members: Array<GitlabUser>): Promise<Array<GitlabUser>> {
         const exercises: Array<Exercise> | undefined = await ExerciseManager.getFromAssignment(assignment.name, { members: true });
         const reachedLimitUsers: Array<GitlabUser> = [];
         if ( exercises ) {
-            for ( const member of params.members ) {
+            for ( const member of members ) {
                 const exerciseCount: number = exercises.filter(exercise => exercise.members.findIndex(exerciseMember => exerciseMember.id === member.id) !== -1).length;
                 if ( exerciseCount >= Config.exercise.maxPerAssignment ) {
                     reachedLimitUsers.push(member);
                 }
             }
         }
-        if ( reachedLimitUsers.length > 0 ) {
-            return req.session.sendResponse(res, StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE, reachedLimitUsers, 'Max exercise per assignment reached', DojoStatusCode.MAX_EXERCISE_PER_ASSIGNMENT_REACHED);
-        }
 
+        return reachedLimitUsers;
+    }
 
-        const exerciseId: string = uuidv4();
-        const secret: string = uuidv4();
+    private async createExerciseRepository(assignment: Assignment, members: Array<GitlabUser>, exerciseId: string, req: express.Request, res: express.Response): Promise<GitlabRepository | undefined> {
         let repository!: GitlabRepository;
 
         let suffix: number = 0;
         do {
             try {
-                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);
+                repository = await GitlabManager.forkRepository((assignment.gitlabCreationInfo as unknown as GitlabRepository).id, this.getExerciseName(assignment, 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);
                 break;
             } 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' ) {
+                    if ( error.response?.data.message.name && error.response.data.message.name === 'has already been taken' ) {
                         suffix++;
                     } else {
-                        return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown gitlab error while forking repository', DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR);
+                        req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown gitlab error while forking repository', DojoStatusCode.EXERCISE_CREATION_GITLAB_ERROR);
+                        return;
                     }
                 } else {
-                    return req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while forking repository', DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR);
+                    req.session.sendResponse(res, StatusCodes.INTERNAL_SERVER_ERROR, {}, 'Unknown error while forking repository', DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR);
+                    return;
                 }
             }
         } while ( suffix < Config.exercise.maxSameName );
 
         if ( suffix >= Config.exercise.maxSameName ) {
             logger.error('Max exercise with same name reached');
-            return res.status(StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE).send();
+            req.session.sendResponse(res, StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE, undefined, 'Max exercise per assignment reached', DojoStatusCode.MAX_EXERCISE_PER_ASSIGNMENT_REACHED);
+            return;
+        }
+
+        return repository;
+    }
+
+    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 assignment: Assignment = req.boundParams.assignment!;
+
+
+        const reachedLimitUsers: Array<GitlabUser> = await this.checkExerciseLimit(assignment, params.members);
+        if ( reachedLimitUsers.length > 0 ) {
+            return req.session.sendResponse(res, StatusCodes.INSUFFICIENT_SPACE_ON_RESOURCE, reachedLimitUsers, 'Max exercise per assignment reached', DojoStatusCode.MAX_EXERCISE_PER_ASSIGNMENT_REACHED);
+        }
+
+
+        const exerciseId: string = uuidv4();
+        const secret: string = uuidv4();
+        const repository: GitlabRepository | undefined = await this.createExerciseRepository(assignment, params.members, exerciseId, req, res);
+
+        if ( !repository ) {
+            return;
         }
 
         await new Promise(resolve => setTimeout(resolve, Config.gitlab.repository.timeoutAfterCreation));
@@ -219,7 +240,7 @@ class ExerciseRoutes implements RoutesManager {
         const immutablePaths = dojoAssignmentFile.immutable.map(fileDescriptor => fileDescriptor.path);
 
         await Promise.all(repoTree.map(async gitlabTreeFile => {
-            if ( gitlabTreeFile.type == GitlabTreeFileType.BLOB ) {
+            if ( gitlabTreeFile.type === GitlabTreeFileType.BLOB ) {
                 for ( const immutablePath of immutablePaths ) {
                     if ( gitlabTreeFile.path.startsWith(immutablePath) ) {
                         immutableFiles.push(await GitlabManager.getFile(req.boundParams.exercise!.assignment.gitlabId, gitlabTreeFile.path));
diff --git a/ExpressAPI/src/shared b/ExpressAPI/src/shared
index 9e3f29d..da76a3b 160000
--- a/ExpressAPI/src/shared
+++ b/ExpressAPI/src/shared
@@ -1 +1 @@
-Subproject commit 9e3f29d2f313ef96944a199da0db39f1827c496a
+Subproject commit da76a3b35471ec1e9862ae3b57f5905a909058b5
diff --git a/sonar-project.properties b/sonar-project.properties
index f59019d..51242d1 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,2 +1,3 @@
 sonar.projectKey=DojoBackendAPI
-sonar.qualitygate.wait=true
\ No newline at end of file
+sonar.qualitygate.wait=true
+sonar.exclusions=ExpressAPI/prisma/seed.ts
\ No newline at end of file
-- 
GitLab