-
michael.minelli authoredmichael.minelli authored
ExerciseResultsSanitizerAndValidator.ts 5.09 KiB
import { TypedEmitter } from 'tiny-typed-emitter';
import ExerciseRunningEvents from '../../types/Dojo/ExerciseRunningEvents.js';
import ExerciseCheckerError from '../../../shared/types/Dojo/ExerciseCheckerError.js';
import path from 'node:path';
import ClientsSharedConfig from '../../config/ClientsSharedConfig.js';
import Toolbox from '../../../shared/helpers/Toolbox.js';
import * as fs from 'fs-extra';
import ExerciseResultsFile from '../../../shared/types/Dojo/ExerciseResultsFile.js';
import JSON5 from 'json5';
import Json5FileValidator from '../../../shared/helpers/Json5FileValidator.js';
class ExerciseResultsSanitizerAndValidator {
readonly events: TypedEmitter<ExerciseRunningEvents> = new TypedEmitter<ExerciseRunningEvents>();
public exerciseResults: ExerciseResultsFile = {};
private resultsFilePath: string = '';
private readonly folderResultsDojo: string;
private readonly folderResultsExercise: string;
private readonly containerExitCode: number;
constructor(folderResultsDojo: string, folderResultsExercise: string, containerExitCode: number) {
this.folderResultsDojo = folderResultsDojo;
this.folderResultsExercise = folderResultsExercise;
this.containerExitCode = containerExitCode;
}
private resultsFileSanitization() {
this.events.emit('step', 'RESULTS_FILE_SANITIZATION', 'Sanitizing results file');
if ( this.exerciseResults.success === undefined ) {
this.exerciseResults.success = this.containerExitCode === 0;
}
if ( this.exerciseResults.containerExitCode === undefined ) {
this.exerciseResults.containerExitCode = this.containerExitCode;
}
this.events.emit('endStep', 'RESULTS_FILE_SANITIZATION', 'Results file sanitized', false);
}
private async resultsFileProvided(resultFilePath: string): Promise<boolean> {
// Results file schema validation
{
this.events.emit('step', 'VALIDATE_RESULTS_FILE', 'Validating results file schema');
const validationResults = Json5FileValidator.validateFile(ExerciseResultsFile, resultFilePath);
if ( !validationResults.isValid ) {
this.events.emit('endStep', 'VALIDATE_RESULTS_FILE', `Results file is not valid. Here are the errors :\n${ validationResults.error }`, true);
this.events.emit('finished', false, ExerciseCheckerError.EXERCISE_RESULTS_FILE_SCHEMA_NOT_VALID);
return false;
}
this.exerciseResults = validationResults.content ?? {};
this.events.emit('endStep', 'VALIDATE_RESULTS_FILE', 'Results file is valid', false);
}
// Results file content sanitization
this.resultsFileSanitization();
// Results folder size
// ATTENTION: This test is at the end because even if it fail the local execution will continue and we need the other test above to be done
{
this.events.emit('step', 'CHECK_SIZE', 'Validating results folder size');
const resultsFolderSize = await Toolbox.fs.getTotalSize(this.folderResultsExercise);
if ( resultsFolderSize > ClientsSharedConfig.exerciseResultsFolderMaxSizeInBytes ) {
this.events.emit('endStep', 'CHECK_SIZE', `Results folder size is too big (bigger than ${ ClientsSharedConfig.exerciseResultsFolderMaxSizeInBytes / 1000000 } MB)`, true);
this.events.emit('finished', false, ExerciseCheckerError.EXERCISE_RESULTS_FOLDER_TOO_BIG);
return false;
}
this.events.emit('endStep', 'CHECK_SIZE', 'Results folder size is in bounds', false);
}
return true;
}
run() {
void (async () => {
// Results file existence
this.events.emit('step', 'CHECK_RESULTS_FILE_EXIST', 'Checking if results file exists');
const resultsFileOriginPath = path.join(this.folderResultsExercise, ClientsSharedConfig.filenames.results);
this.resultsFilePath = path.join(this.folderResultsDojo, ClientsSharedConfig.filenames.results);
let result: boolean = true;
if ( fs.existsSync(resultsFileOriginPath) ) {
this.events.emit('endStep', 'CHECK_RESULTS_FILE_EXIST', 'Results file found', false);
result = await this.resultsFileProvided(resultsFileOriginPath);
if ( result ) {
fs.rmSync(resultsFileOriginPath, { force: true });
} else {
fs.moveSync(resultsFileOriginPath, this.resultsFilePath, { overwrite: true });
}
} else {
this.events.emit('endStep', 'CHECK_RESULTS_FILE_EXIST', 'Results file not found', false);
this.resultsFileSanitization();
}
if ( result ) {
fs.writeFileSync(this.resultsFilePath, JSON5.stringify(this.exerciseResults, { space: 4 }), 'utf8');
this.events.emit('finished', true, 0);
}
})();
}
}
export default ExerciseResultsSanitizerAndValidator;