Skip to content
Snippets Groups Projects
Commit 5adda6d7 authored by joel.vonderwe's avatar joel.vonderwe
Browse files

Add route to test sonar qualities

parent 8b1a2327
No related branches found
No related tags found
No related merge requests found
Pipeline #32209 passed
...@@ -24,10 +24,12 @@ servers: ...@@ -24,10 +24,12 @@ servers:
tags: tags:
- name: General - name: General
description: '' description: ''
- name: Sonar
description: Routes that are used to manage SonarQube information
- name: Session - name: Session
description: Routes that are used to manage the user's session description: Routes that are used to manage the user's session
- name: Gitlab - name: Gitlab
description: Routes that are used to provide Gitlab informations description: Routes that are used to provide Gitlab information
- name: Assignment - name: Assignment
description: Routes that are used to manage assignments description: Routes that are used to manage assignments
- name: Exercise - name: Exercise
...@@ -48,10 +50,10 @@ paths: ...@@ -48,10 +50,10 @@ paths:
description: OK description: OK
default: default:
$ref: '#/components/responses/ERROR' $ref: '#/components/responses/ERROR'
/sonar: /sonar/info:
get: get:
tags: tags:
- General - Sonar
summary: Check sonar status summary: Check sonar status
description: This route can be used to check if the server supports sonar and if the integration is enabled. description: This route can be used to check if the server supports sonar and if the integration is enabled.
responses: responses:
...@@ -80,6 +82,55 @@ paths: ...@@ -80,6 +82,55 @@ paths:
description: OK description: OK
default: default:
$ref: '#/components/responses/ERROR' $ref: '#/components/responses/ERROR'
/sonar/testqualities:
post:
tags:
- Sonar
summary: Test existence and validity of a quality gate and quality profiles
description: |
This route should be used at assignment creation to test existence and validity of a quality gate and quality profiles before creating the assignment
**🔒 Security needs:** TeachingStaff or Admin roles
security:
- Clients_Token: [ ]
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
gate:
type: string
profiles:
type: string
format: json
description: JSON string array of quality profiles
required:
- gate
- profiles
responses:
'200':
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DojoBackendResponse'
- type: object
properties:
data:
type: object
properties:
valid:
type: boolean
badGate:
type: string
description: Name of the gate if invalid, or null
badProfiles:
type: array
items: string
description: List of invalid profiles
description: OK
default:
$ref: '#/components/responses/ERROR'
/login: /login:
post: post:
tags: tags:
......
...@@ -148,7 +148,6 @@ class GitlabManager { ...@@ -148,7 +148,6 @@ class GitlabManager {
} }
async addRepositoryVariable(repoId: number, key: string, value: string, isProtected: boolean, isMasked: boolean): Promise<GitlabMember> { async addRepositoryVariable(repoId: number, key: string, value: string, isProtected: boolean, isMasked: boolean): Promise<GitlabMember> {
console.log(key, value);
const response = await axios.post<GitlabMember>(this.getApiUrl(GitlabRoute.REPOSITORY_VARIABLES_ADD).replace('{{id}}', String(repoId)), { const response = await axios.post<GitlabMember>(this.getApiUrl(GitlabRoute.REPOSITORY_VARIABLES_ADD).replace('{{id}}', String(repoId)), {
key : key, key : key,
variable_type: 'env_var', variable_type: 'env_var',
......
...@@ -47,11 +47,13 @@ class SonarManager { ...@@ -47,11 +47,13 @@ class SonarManager {
})).data; })).data;
} }
private async executeGetRequest<T>(url: string) { private async executeGetRequest<T>(url: string, data?: unknown) {
return (await this.instance.get<T>(url, { return (await this.instance.get<T>(url, {
headers: { headers: {
Authorization: `Basic ${ btoa(SharedConfig.sonar.token + ":") }` Authorization: `Basic ${ btoa(SharedConfig.sonar.token + ":") }`
} },
params: data
})).data; })).data;
} }
...@@ -128,6 +130,31 @@ class SonarManager { ...@@ -128,6 +130,31 @@ class SonarManager {
const resp = await this.executeGetRequest<{ languages: { key: string, name: string }[]}>(this.getApiUrl(SonarRoute.GET_LANGUAGES)) const resp = await this.executeGetRequest<{ languages: { key: string, name: string }[]}>(this.getApiUrl(SonarRoute.GET_LANGUAGES))
return resp.languages.map(l => l.key) return resp.languages.map(l => l.key)
} }
async testQualityGate(gateName: string) {
try {
await this.executeGetRequest(this.getApiUrl(SonarRoute.TEST_GATE), { name: gateName });
return true;
} catch ( e ) {
return false;
}
}
async testQualityProfile(profileName: string, language: string) {
try {
const formData = new FormData();
formData.append('language', language);
formData.append('qualityProfile', profileName);
const resp = await this.executeGetRequest<{ profiles: { key: string, name: string, language: string }[] }>(
this.getApiUrl(SonarRoute.TEST_PROFILE), formData
);
return (resp.profiles.length > 0 && resp.profiles.some(p => p.name === profileName && p.language === language))
} catch ( e ) {
return false;
}
}
} }
export default new SonarManager(); export default new SonarManager();
\ No newline at end of file
...@@ -5,6 +5,7 @@ import SessionRoutes from './SessionRoutes'; ...@@ -5,6 +5,7 @@ import SessionRoutes from './SessionRoutes';
import AssignmentRoutes from './AssignmentRoutes'; import AssignmentRoutes from './AssignmentRoutes';
import GitlabRoutes from './GitlabRoutes'; import GitlabRoutes from './GitlabRoutes';
import ExerciseRoutes from './ExerciseRoutes'; import ExerciseRoutes from './ExerciseRoutes';
import SonarRoutes from './SonarRoutes';
class AdminRoutesManager implements RoutesManager { class AdminRoutesManager implements RoutesManager {
...@@ -14,6 +15,7 @@ class AdminRoutesManager implements RoutesManager { ...@@ -14,6 +15,7 @@ class AdminRoutesManager implements RoutesManager {
GitlabRoutes.registerOnBackend(backend); GitlabRoutes.registerOnBackend(backend);
AssignmentRoutes.registerOnBackend(backend); AssignmentRoutes.registerOnBackend(backend);
ExerciseRoutes.registerOnBackend(backend); ExerciseRoutes.registerOnBackend(backend);
SonarRoutes.registerOnBackend(backend);
} }
} }
......
...@@ -2,15 +2,12 @@ import { Express } from 'express-serve-static-core'; ...@@ -2,15 +2,12 @@ import { Express } from 'express-serve-static-core';
import express from 'express'; import express from 'express';
import { StatusCodes } from 'http-status-codes'; import { StatusCodes } from 'http-status-codes';
import RoutesManager from '../express/RoutesManager'; import RoutesManager from '../express/RoutesManager';
import SharedSonarManager from '../shared/managers/SharedSonarManager';
import SonarManager from '../managers/SonarManager';
class BaseRoutes implements RoutesManager { class BaseRoutes implements RoutesManager {
registerOnBackend(backend: Express) { registerOnBackend(backend: Express) {
backend.get('/', this.homepage.bind(this)); backend.get('/', this.homepage.bind(this));
backend.get('/health_check', this.healthCheck.bind(this)); backend.get('/health_check', this.healthCheck.bind(this));
backend.get('/sonar', this.sonar.bind(this));
} }
private async homepage(req: express.Request, res: express.Response) { private async homepage(req: express.Request, res: express.Response) {
...@@ -20,14 +17,6 @@ class BaseRoutes implements RoutesManager { ...@@ -20,14 +17,6 @@ class BaseRoutes implements RoutesManager {
private async healthCheck(req: express.Request, res: express.Response) { private async healthCheck(req: express.Request, res: express.Response) {
return req.session.sendResponse(res, StatusCodes.OK); return req.session.sendResponse(res, StatusCodes.OK);
} }
private async sonar(req: express.Request, res: express.Response) {
const data = {
sonarEnabled: await SharedSonarManager.isSonarSupported(),
languages: await SonarManager.getLanguages()
};
return req.session.sendResponse(res, StatusCodes.OK, data);
}
} }
......
import { Express } from 'express-serve-static-core';
import express from 'express';
import { StatusCodes } from 'http-status-codes';
import RoutesManager from '../express/RoutesManager';
import SharedSonarManager from '../shared/managers/SharedSonarManager';
import SonarManager from '../managers/SonarManager';
import SecurityMiddleware from '../middlewares/SecurityMiddleware';
import SecurityCheckType from '../types/SecurityCheckType';
import * as ExpressValidator from 'express-validator';
import DojoValidators from '../helpers/DojoValidators';
import ParamsValidatorMiddleware from '../middlewares/ParamsValidatorMiddleware';
class SonarRoutes implements RoutesManager {
private readonly qualitiesValidator: ExpressValidator.Schema = {
gate : {
trim : true,
notEmpty: false
},
profiles : {
trim : true,
notEmpty : false,
customSanitizer: DojoValidators.jsonSanitizer
}
};
registerOnBackend(backend: Express) {
backend.get('/sonar/info', this.sonar.bind(this));
backend.post('/sonar/testqualities', SecurityMiddleware.check(true, SecurityCheckType.TEACHING_STAFF), ParamsValidatorMiddleware.validate(this.qualitiesValidator), this.testQualities.bind(this));
}
private async sonar(req: express.Request, res: express.Response) {
const data = {
sonarEnabled: await SharedSonarManager.isSonarSupported(),
languages: await SonarManager.getLanguages()
};
return req.session.sendResponse(res, StatusCodes.OK, data);
}
private async testQualities(req: express.Request, res: express.Response) {
const params: {
gate: string | undefined, profiles: string[]
} = req.body;
console.log(params);
let gateOk = true;
if ((params.gate ?? "") !== "") {
gateOk = await SonarManager.testQualityGate(params.gate ?? "")
}
let profilesOk = true;
const badProfiles = [];
for ( const profile of params.profiles ) {
try {
const [ lang, name ] = profile.trim().split('/');
if ( !await SonarManager.testQualityProfile(name, lang) ) {
profilesOk = false;
badProfiles.push(profile);
}
} catch (e) {
profilesOk = false;
badProfiles.push(profile);
}
}
console.log(gateOk, profilesOk);
const data = {
valid: gateOk && profilesOk,
badProfiles: badProfiles,
badGate: (gateOk ? null : params.gate)
};
console.log(data);
return req.session.sendResponse(res, StatusCodes.OK, data);
}
}
export default new SonarRoutes();
Subproject commit 44b2ae365423ca96ee035d0c21bf36a27141aa79 Subproject commit 4d1e63ebbbe7e6fec1de74d79a2919047eea5775
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment