From 503a2ada14b53100df7863f8fd1ab1c8552d417a Mon Sep 17 00:00:00 2001 From: Joel von der Weid <joel.von-der-weid@hesge.ch> Date: Tue, 14 May 2024 15:50:31 +0200 Subject: [PATCH] Add quality gate and profiles to assignments and exercises --- ExpressAPI/prisma/seed.ts | 6 ++++-- ExpressAPI/src/helpers/GlobalHelper.ts | 10 ++++++++-- ExpressAPI/src/managers/SonarManager.ts | 15 +++++++++++---- ExpressAPI/src/routes/AssignmentRoutes.ts | 3 ++- ExpressAPI/src/routes/ExerciseRoutes.ts | 2 +- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/ExpressAPI/prisma/seed.ts b/ExpressAPI/prisma/seed.ts index 9154ef5..f73e71a 100644 --- a/ExpressAPI/prisma/seed.ts +++ b/ExpressAPI/prisma/seed.ts @@ -341,7 +341,8 @@ async function assignments() { 'autoclose_referenced_issues' : true }, gitlabLastInfoDate: new Date('2021-10-14T14:20:15.239Z'), - published : false + published : false, + secret : '77B9BB29-82F8-4D1F-B6D2-A201ABFB8509' } }); await db.assignment.upsert({ @@ -597,7 +598,8 @@ async function assignments() { 'autoclose_referenced_issues' : true }, gitlabLastInfoDate: new Date('2023-11-07T20:35:54.692Z'), - published : true + published : true, + secret : '1127A3E7-4461-43B9-85DE-13C2317617AA' } }); } diff --git a/ExpressAPI/src/helpers/GlobalHelper.ts b/ExpressAPI/src/helpers/GlobalHelper.ts index c34aac4..b784505 100644 --- a/ExpressAPI/src/helpers/GlobalHelper.ts +++ b/ExpressAPI/src/helpers/GlobalHelper.ts @@ -5,10 +5,12 @@ import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode.js'; import { StatusCodes } from 'http-status-codes'; import { GitbeakerRequestError } from '@gitbeaker/requester-utils'; import * as Gitlab from '@gitbeaker/rest'; -import { AxiosError } from 'axios'; +import SonarProjectCreation from '../shared/types/Sonar/SonarProjectCreation'; +import SonarManager from '../managers/SonarManager'; + class GlobalHelper { - repoCreationFnExecCreator(req: express.Request, res: express.Response, gitlabError: DojoStatusCode, internalError: DojoStatusCode, repositoryToRemove?: Gitlab.ProjectSchema) { + repoCreationFnExecCreator(req: express.Request, res: express.Response, gitlabError: DojoStatusCode, internalError: DojoStatusCode, repositoryToRemove?: Gitlab.ProjectSchema, sonarToRemove?: SonarProjectCreation) { return async (toExec: () => Promise<unknown>, errorMessage?: string) => { try { return await toExec(); @@ -21,6 +23,10 @@ class GlobalHelper { if ( repositoryToRemove ) { await GitlabManager.deleteRepository(repositoryToRemove.id); } + + if ( sonarToRemove ) { + await SonarManager.deleteProject(sonarToRemove.project.key); + } } catch ( deleteError ) { logger.error('Repository deletion error'); logger.error(JSON.stringify(deleteError)); diff --git a/ExpressAPI/src/managers/SonarManager.ts b/ExpressAPI/src/managers/SonarManager.ts index 88320a4..9c8946e 100644 --- a/ExpressAPI/src/managers/SonarManager.ts +++ b/ExpressAPI/src/managers/SonarManager.ts @@ -88,14 +88,14 @@ class SonarManager { return await GlobalHelper.repositoryCreationError('Sonar error', undefined, req, res, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, gitlabRepository); } } catch ( error ) { - return await GlobalHelper.repositoryCreationError('Sonar project creation error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, gitlabRepository); + return await GlobalHelper.repositoryCreationError('Sonar project creation error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, gitlabRepository, sonarProject); } // Add gate and profiles to sonar project if ( qualityGate != undefined && qualityGate != "" ) { try { await this.addQualityGate(sonarProject.project.key, qualityGate); } catch ( error ) { - return await GlobalHelper.repositoryCreationError('Sonar gate error', error, req, res, DojoStatusCode.ASSIGNMENT_SONAR_GATE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_GATE_NOT_FOUND, gitlabRepository); + return await GlobalHelper.repositoryCreationError('Sonar gate error', error, req, res, DojoStatusCode.ASSIGNMENT_SONAR_GATE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_GATE_NOT_FOUND, gitlabRepository, sonarProject); } } @@ -106,10 +106,10 @@ class SonarManager { if (lang.trim() != '' && name.trim() != '') { await this.addQualityProfile(sonarProject.project.key, name.trim(), lang.trim()); } else { - return await GlobalHelper.repositoryCreationError('Sonar profile invalid', undefined, req, res, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, gitlabRepository); + return await GlobalHelper.repositoryCreationError('Sonar profile invalid', undefined, req, res, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, gitlabRepository, sonarProject); } } catch ( error ) { - return await GlobalHelper.repositoryCreationError('Sonar profile not found', error, req, res, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, gitlabRepository); + return await GlobalHelper.repositoryCreationError('Sonar profile not found', error, req, res, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, gitlabRepository, sonarProject); } } } @@ -117,6 +117,13 @@ class SonarManager { return sonarProject; } + async deleteProject(projectKey: string) { + const formData = new FormData(); + formData.append('project', projectKey); + + return await this.executePostRequest<SonarProjectCreation>(this.getApiUrl(SonarRoute.PROJECT_DELETE), formData) + } + async getLanguages() { const resp = await this.executeGetRequest<{ languages: { key: string, name: string }[]}>(this.getApiUrl(SonarRoute.GET_LANGUAGES)) return resp.languages.map(l => l.key) diff --git a/ExpressAPI/src/routes/AssignmentRoutes.ts b/ExpressAPI/src/routes/AssignmentRoutes.ts index e7d8c13..ee123f2 100644 --- a/ExpressAPI/src/routes/AssignmentRoutes.ts +++ b/ExpressAPI/src/routes/AssignmentRoutes.ts @@ -187,7 +187,7 @@ class AssignmentRoutes implements RoutesManager { try { await repoCreationFnExec(() => GitlabManager.protectBranch(repository.id, '*', true, Gitlab.AccessLevel.DEVELOPER, Gitlab.AccessLevel.DEVELOPER, Gitlab.AccessLevel.ADMIN), 'Branch protection modification error'); - await repoCreationFnExec(() => GitlabManager.addRepositoryVariable(repository.id, 'DOJO_ASSIGNMENT_NAME', repository.name, false, true), 'Add repo variable "DOJO_ASSIGNMENT_NAME" error'); + await repoCreationFnExec(() => GitlabManager.addRepositoryVariable(repository.id, 'DOJO_ASSIGNMENT_NAME', repository.name, false, false), 'Add repo variable "DOJO_ASSIGNMENT_NAME" error'); await repoCreationFnExec(() => GitlabManager.addRepositoryVariable(repository.id, 'DOJO_ASSIGNMENT_SECRET', secret, false, true), 'Add repo variable "DOJO_ASSIGNMENT_SECRET" error'); await repoCreationFnExec(() => GitlabManager.addRepositoryBadge(repository.id, Config.gitlab.badges.pipeline.link, Config.gitlab.badges.pipeline.imageUrl, 'Pipeline Status'), 'Pipeline badge addition error'); await repoCreationFnExec(() => GitlabManager.deleteFile(repository.id, '.gitlab-ci.yml', 'Remove .gitlab-ci.yml')); @@ -221,6 +221,7 @@ class AssignmentRoutes implements RoutesManager { sonarGate : params.sonarGate, sonarProfiles : params.sonarProfiles, language : Language[params.language as keyof typeof Language], + secret : secret, staff : { connectOrCreate: [ ...params.members.map(gitlabUser => { return { diff --git a/ExpressAPI/src/routes/ExerciseRoutes.ts b/ExpressAPI/src/routes/ExerciseRoutes.ts index 6221ede..269cedf 100644 --- a/ExpressAPI/src/routes/ExerciseRoutes.ts +++ b/ExpressAPI/src/routes/ExerciseRoutes.ts @@ -245,6 +245,7 @@ class ExerciseRoutes implements RoutesManager { const exerciseId: string = uuidv4(); const secret: string = uuidv4(); const repository: Gitlab.ProjectSchema | undefined = await this.createExerciseRepository(assignment, params.members, exerciseId, req, res); + let sonarProject: SonarProjectCreation | undefined = undefined; if ( !repository ) { return; @@ -269,7 +270,6 @@ class ExerciseRoutes implements RoutesManager { await repoCreationFnExec(async () => Promise.all([ ...new Set([ ...assignment.staff, ...params.members ].map(member => member.id)) ].map(GlobalHelper.addRepoMember(repository.id))), 'Add repository members error'); // Create Sonar project - let sonarProject: SonarProjectCreation | undefined = undefined; if ( assignment.useSonar && assignment.sonarProfiles != null ) { const profiles: string[] = JSON.parse(assignment.sonarProfiles as string); sonarProject = await GlobalHelper.repoCreationFnExecCreator(req, res, DojoStatusCode.EXERCISE_CREATION_SONAR_ERROR, DojoStatusCode.EXERCISE_CREATION_INTERNAL_ERROR, repository)(() => SonarManager.createProjectWithQualities(repository, assignment.sonarGate, profiles, req, res), 'Sonar project creation error') as SonarProjectCreation; -- GitLab