diff --git a/helpers/ArchiveHelper.ts b/helpers/ArchiveHelper.ts index f4449d6e8f4953227e0b422bb0a647a85432351c..b10ec6531bbd239509582a04fcf0b17e01f06e11 100644 --- a/helpers/ArchiveHelper.ts +++ b/helpers/ArchiveHelper.ts @@ -8,26 +8,26 @@ import zlib from 'zlib'; class ArchiveHelper { private async explore(absoluteBasePath: string, rootPath: string, pack: tar.Pack) { - for ( let file of await fs.promises.readdir(rootPath) ) { - if ( file === 'output.tar' ) { - continue; - } - file = path.join(rootPath, file); - const stat = await fs.promises.stat(file); - if ( stat.isDirectory() ) { - await this.explore(absoluteBasePath, file, pack); - continue; - } - const entry = pack.entry({ - name: file.replace(absoluteBasePath, ''), - size: stat.size - }, (err) => { - if ( err ) { - throw err; + for ( const file of await fs.promises.readdir(rootPath) ) { + let filename = file; + if ( filename !== 'output.tar' ) { + filename = path.join(rootPath, filename); + const stat = await fs.promises.stat(filename); + if ( stat.isDirectory() ) { + await this.explore(absoluteBasePath, filename, pack); + } else { + const entry = pack.entry({ + name: filename.replace(absoluteBasePath, ''), + size: stat.size + }, err => { + if ( err ) { + throw err; + } + }); + const readStream = fs.createReadStream(filename); + readStream.pipe(entry); } - }); - const stream = fs.createReadStream(file); - stream.pipe(entry); + } } } @@ -55,7 +55,7 @@ class ArchiveHelper { await this.compress(folderPath, tarDataStream); - data = await (new Promise((resolve) => { + data = await (new Promise(resolve => { tarDataStream.on('close', () => { resolve(data); }); diff --git a/helpers/Dojo/SharedAssignmentHelper.ts b/helpers/Dojo/SharedAssignmentHelper.ts index b103f8cc526ed60a9aa6b7c5276557bcf1ab6635..f7547cc97f51c4d13d15a95f80a6bd8844ef29bc 100644 --- a/helpers/Dojo/SharedAssignmentHelper.ts +++ b/helpers/Dojo/SharedAssignmentHelper.ts @@ -8,15 +8,14 @@ import Json5FileValidator from '../Json5FileValidator'; class SharedAssignmentHelper { validateDescriptionFile(filePathOrStr: string, isFile: boolean = true, version: number = 1): { content: AssignmentFile | undefined, isValid: boolean, error: string | null } { - switch ( version ) { - case 1: - return Json5FileValidator.validateFile(AssignmentFile, filePathOrStr, isFile); - default: - return { - content: undefined, - isValid: false, - error : `Version ${ version } not supported` - }; + if ( version === 1 ) { + return Json5FileValidator.validateFile(AssignmentFile, filePathOrStr, isFile); + } else { + return { + content: undefined, + isValid: false, + error : `Version ${ version } not supported` + }; } } @@ -24,7 +23,7 @@ class SharedAssignmentHelper { const pipelines = await SharedGitlabManager.getRepositoryPipelines(repositoryId, 'main'); if ( pipelines.length > 0 ) { const lastPipeline = pipelines[0]; - if ( lastPipeline.status != GitlabPipelineStatus.SUCCESS ) { + if ( lastPipeline.status !== GitlabPipelineStatus.SUCCESS ) { return { isPublishable: false, lastPipeline : lastPipeline, diff --git a/helpers/LazyVal.ts b/helpers/LazyVal.ts index 4613c4b535706f5853b060279ce46a957e3c3030..a06ba8b1a0cf8cb20ff87f7862a2e4c5d5f1b854 100644 --- a/helpers/LazyVal.ts +++ b/helpers/LazyVal.ts @@ -1,10 +1,13 @@ class LazyVal<T> { private val: T | undefined = undefined; + private readonly valLoader: () => Promise<T> | T; - constructor(private valLoader: () => Promise<T> | T) {} + constructor(valLoader: () => Promise<T> | T) { + this.valLoader = valLoader; + } get value(): Promise<T> { - return new Promise<T>((resolve) => { + return new Promise<T>(resolve => { if ( this.val === undefined ) { Promise.resolve(this.valLoader()).then((value: T) => { this.val = value; diff --git a/helpers/Toolbox.ts b/helpers/Toolbox.ts index 8a76bebb202e6bcbe46944e562924effdeb26afe..cda569340b7ec53d906e353555fa289fa920d9b8 100644 --- a/helpers/Toolbox.ts +++ b/helpers/Toolbox.ts @@ -14,8 +14,9 @@ class Toolbox { const files = await fs.readdir(dirPath); await Promise.all(files.map(async file => { - if ( (await fs.stat(dirPath + '/' + file)).isDirectory() ) { - arrayOfFiles = await this.getAllFiles(dirPath + '/' + file, arrayOfFiles); + const filePath = path.join(dirPath, file); + if ( (await fs.stat(filePath)).isDirectory() ) { + arrayOfFiles = await this.getAllFiles(filePath, arrayOfFiles); } else { arrayOfFiles.push(path.join(dirPath, file)); } diff --git a/helpers/TypeScriptExtensions.ts b/helpers/TypeScriptExtensions.ts index fd730ad37daa50a1b6e855efaf3c48ce739faa05..0fc9c70f416cd51c3de2995e2b4e93df47092672 100644 --- a/helpers/TypeScriptExtensions.ts +++ b/helpers/TypeScriptExtensions.ts @@ -64,9 +64,7 @@ function registerStringCapitalizeName() { function registerStringConvertWithEnvVars() { String.prototype.convertWithEnvVars = function (this: string): string { - return this.replace(/\${?([a-zA-Z0-9_]+)}?/g, (_match: string, p1: string) => { - return process.env[p1] || ''; - }); + return this.replace(/\${?([a-zA-Z0-9_]+)}?/g, (_match: string, p1: string) => process.env[p1] || ''); }; } diff --git a/helpers/recursiveFilesStats/RecursiveFilesStats.ts b/helpers/recursiveFilesStats/RecursiveFilesStats.ts index a571aaac797de782ee6bc95d33ad9e75ee03b647..a71bffd40bdc53efee7b4a153f7e42a1cf8f9c02 100644 --- a/helpers/recursiveFilesStats/RecursiveFilesStats.ts +++ b/helpers/recursiveFilesStats/RecursiveFilesStats.ts @@ -51,32 +51,36 @@ class RecursiveFilesStats { return this.getFiles(`${ path.resolve(rootPath) }/`, rootPath, options, [], callback); } - private async getFiles(absoluteBasePath: string, rootPath: string, options: RecursiveReaddirFilesOptions = {}, files: IFileDirStat[] = [], callback?: Callback): Promise<IFileDirStat[]> { - const { - ignored, include, exclude, filter - } = options; + private async getFilesDirsStat(rootPath: string, options: RecursiveReaddirFilesOptions): Promise<Array<IFileDirStat>> { const filesData = await fs.promises.readdir(rootPath); - const fileDir: IFileDirStat[] = filesData.map((file) => ({ - name: file, path: path.join(rootPath, file) - })).filter((item) => { - if ( include && include.test(item.path) ) { + return filesData.map(file => ({ + name: file, + path: path.join(rootPath, file) + })).filter(item => { + if ( options.include && options.include.test(item.path) ) { return true; } - if ( exclude && exclude.test(item.path) ) { + if ( options.exclude && options.exclude.test(item.path) ) { return false; } - if ( ignored ) { - return !ignored.test(item.path); + if ( options.ignored ) { + return !options.ignored.test(item.path); } return true; }); + } + + private async getFiles(absoluteBasePath: string, rootPath: string, options: RecursiveReaddirFilesOptions = {}, files: IFileDirStat[] = [], callback?: Callback): Promise<IFileDirStat[]> { + const fileDir: Array<IFileDirStat> = await this.getFilesDirsStat(rootPath, options); + if ( callback ) { - fileDir.map(async (item: IFileDirStat) => { - const stat = await this.getStat(item.path, absoluteBasePath, options); - if ( stat.isDirectory!() ) { - await this.getFiles(absoluteBasePath, item.path, options, [], callback); - } - callback(item.path, stat); + fileDir.forEach(item => { + this.getStat(item.path, absoluteBasePath, options).then(stat => { + if ( stat.isDirectory!() ) { + this.getFiles(absoluteBasePath, item.path, options, [], callback).then(); + } + callback(item.path, stat); + }); }); } else { await Promise.all(fileDir.map(async (item: IFileDirStat) => { @@ -89,9 +93,9 @@ class RecursiveFilesStats { } })); } - return files.filter((item) => { - if ( filter && typeof filter === 'function' ) { - return filter(item); + return files.filter(item => { + if ( options.filter && typeof options.filter === 'function' ) { + return options.filter(item); } return true; }); @@ -124,10 +128,7 @@ class RecursiveFilesStats { delete stat.ctimeMs; delete stat.birthtimeMs; delete stat.atime; - //delete stat.mtime; delete stat.ctime; - //delete stat.birthtime; - //delete stat.mode; } return stat; diff --git a/logging/WinstonLogger.ts b/logging/WinstonLogger.ts index 941e5388ed95b1f281c21f5d2bf2c0c9217ae10a..fc1a3d6da940e31a10a5d2f8b219ce23d10a2504 100644 --- a/logging/WinstonLogger.ts +++ b/logging/WinstonLogger.ts @@ -23,7 +23,11 @@ winston.addColors(colors); const format = winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format(info => ({ ...info, level: info.level.toUpperCase() -}))(), SharedConfig.production ? winston.format.uncolorize() : winston.format.colorize({ all: true }), winston.format.prettyPrint(), winston.format.errors({ stack: true }), winston.format.align(), winston.format.printf((info) => `[${ info.timestamp }] (${ process.pid }) ${ info.level } ${ info.message } ${ info.metadata ? `\n${ JSON.stringify(info.metadata) }` : '' } ${ info.stack ? `\n${ info.stack }` : '' } `)); +}))(), SharedConfig.production ? winston.format.uncolorize() : winston.format.colorize({ all: true }), winston.format.prettyPrint(), winston.format.errors({ stack: true }), winston.format.align(), winston.format.printf(info => { + const metadata = info.metadata ? `\n${ JSON.stringify(info.metadata) }` : ''; + const stack = info.stack ? `\n${ info.stack }` : ''; + return `[${ info.timestamp }] (${ process.pid }) ${ info.level } ${ info.message } ${ metadata } ${ stack } `; +})); const commonTransportOptions = { handleRejections: true, @@ -54,11 +58,11 @@ if ( SharedConfig.production ) { }) ]); } -const logger = winston.createLogger({ - levels, - format, - transports, - exitOnError: false - }); +const WinstonLogger = winston.createLogger({ + levels, + format, + transports, + exitOnError: false + }); -export default logger; +export default WinstonLogger; diff --git a/types/Dojo/AssignmentFile.ts b/types/Dojo/AssignmentFile.ts index df09d53ae249bf3614debd251630eb10e96a5c0b..00796b1f7e8c6aeb00942aa7131bf9a3aaa1d082 100644 --- a/types/Dojo/AssignmentFile.ts +++ b/types/Dojo/AssignmentFile.ts @@ -5,7 +5,7 @@ import { z } from 'zod'; const AssignmentFile = z.object({ dojoAssignmentVersion: z.number(), version : z.number(), - immutable : z.array(ImmutableFileDescriptor.transform(value => value as ImmutableFileDescriptor)), + immutable : z.array(ImmutableFileDescriptor), result : z.object({ container: z.string(), volume : z.string().optional() diff --git a/types/Dojo/DojoStatusCode.ts b/types/Dojo/DojoStatusCode.ts index 16f152daedf7a9f3b8154ea632dce404b8ccc8cf..e7124ae4475bd565bd0813fa087a0cf0ac2da46c 100644 --- a/types/Dojo/DojoStatusCode.ts +++ b/types/Dojo/DojoStatusCode.ts @@ -11,9 +11,12 @@ enum DojoStatusCode { ASSIGNMENT_NOT_PUBLISHED = 205, EXERCISE_CORRECTION_NOT_EXIST = 206, EXERCISE_CORRECTION_ALREADY_EXIST = 207, + ASSIGNMENT_NAME_CONFLICT = 208, EXERCISE_CREATION_GITLAB_ERROR = 302, EXERCISE_CREATION_INTERNAL_ERROR = 303, - MAX_EXERCISE_PER_ASSIGNMENT_REACHED = 304 + MAX_EXERCISE_PER_ASSIGNMENT_REACHED = 304, + GITLAB_TEMPLATE_NOT_FOUND = 401, + GITLAB_TEMPLATE_ACCESS_UNAUTHORIZED = 402, } diff --git a/types/Gitlab/GitlabRoute.ts b/types/Gitlab/GitlabRoute.ts index bdeccc68f582f5f477bd6fb9d8f3fe665680ca5d..15f066174f3483f149fbaa164c9176cec2b43993 100644 --- a/types/Gitlab/GitlabRoute.ts +++ b/types/Gitlab/GitlabRoute.ts @@ -1,11 +1,14 @@ +const projectIdRoute = '/projects/{{id}}'; + + enum GitlabRoute { NOTIFICATION_SETTINGS = '/notification_settings', PROFILE_GET = '/user', USERS_GET = '/users', REPOSITORY_GET = '/projects/{{id}}', REPOSITORY_CREATE = '/projects', // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values - REPOSITORY_DELETE = '/projects/{{id}}', // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values - REPOSITORY_EDIT = '/projects/{{id}}', + REPOSITORY_DELETE = projectIdRoute, // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values + REPOSITORY_EDIT = projectIdRoute, REPOSITORY_FORK = '/projects/{{id}}/fork', REPOSITORY_MEMBER_ADD = '/projects/{{id}}/members', REPOSITORY_MEMBERS_GET = '/projects/{{id}}/members/all',