Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • dojo_project/projects/shared/nodeclientsharedcode
1 result
Select Git revision
Show changes
Commits on Source (47)
Showing with 756 additions and 275 deletions
import axios from 'axios';
import DojoBackendResponse from '../../shared/types/Dojo/DojoBackendResponse';
import ApiRoute from '../types/Dojo/ApiRoute';
interface ClientsConfig {
gitlabUrl: string,
gitlabAccountId: number,
gitlabAccountUsername: string,
loginGitlabClientId: string,
exerciseMaxPerAssignment: number
}
class ClientsSharedConfig {
public apiURL: string;
private static config: ClientsSharedConfig | undefined = undefined;
public assignment: {
filename: string, neededFiles: Array<string>
public apiURL!: string;
public gitlab!: {
URL: string, dojoAccount: { id: number; username: string; };
};
public login!: {
gitlab: {
client: {
id: string
}, url: {
redirect: string, token: string
}
}
};
public gitlab: {
dojoAccount: { id: number; username: string; };
public assignment!: {
filename: string, neededFiles: Array<string>, name: string, secret: string;
};
public readonly dockerCompose: {
public exercise!: {
maxPerAssignment: number
};
public dockerCompose!: {
projectName: string
};
public readonly exerciseResultsFolderMaxSizeInBytes: number;
public exerciseResultsFolderMaxSizeInBytes!: number;
public readonly filenames: {
public filenames!: {
results: string;
};
constructor() {
this.apiURL = process.env.API_URL || '';
public constructor() {
this.login = {
gitlab: {
client: {
id: ''
},
url : {
redirect: '',
token : ''
}
}
};
}
this.assignment = {
filename : process.env.ASSIGNMENT_FILENAME || '',
neededFiles: JSON.parse(process.env.EXERCISE_NEEDED_FILES || '[]')
public envVarGetter(): (envName: string, defaultValue: string) => string {
return (envName: string, defaultValue: string) => {
let value = process.env[envName] ?? defaultValue;
if ( value.includes('{{GITLAB_URL}}') ) {
value = value.replace('{{GITLAB_URL}}', this.gitlab.URL);
}
if ( value.includes('{{GITLAB_ACCOUNT_ID}}') ) {
value = value.replace('{{GITLAB_ACCOUNT_ID}}', String(this.gitlab.dojoAccount.id));
}
if ( value.includes('{{GITLAB_ACCOUNT_USERNAME}}') ) {
value = value.replace('{{GITLAB_ACCOUNT_USERNAME}}', this.gitlab.dojoAccount.username);
}
if ( value.includes('{{LOGIN_GITLAB_CLIENT_ID}}') ) {
value = value.replace('{{LOGIN_GITLAB_CLIENT_ID}}', this.login.gitlab.client.id);
}
return value;
};
}
private async fetchConfigFromApi() {
const downloadedConfig: ClientsConfig = (await axios.get<DojoBackendResponse<ClientsConfig>>(`${ this.apiURL }${ ApiRoute.CLIENTS_CONFIG }`)).data.data;
this.gitlab = {
URL : downloadedConfig.gitlabUrl,
dojoAccount: {
id : Number(process.env.GITLAB_DOJO_ACCOUNT_ID) || -1,
username: process.env.GITLAB_DOJO_ACCOUNT_USERNAME || ''
id : downloadedConfig.gitlabAccountId,
username: downloadedConfig.gitlabAccountUsername
}
};
this.login.gitlab.client.id = downloadedConfig.loginGitlabClientId;
this.exercise = {
maxPerAssignment: downloadedConfig.exerciseMaxPerAssignment
};
}
async init(apiUrl: string) {
this.apiURL = apiUrl;
await this.fetchConfigFromApi();
const getEnvVar = this.envVarGetter();
this.login.gitlab.url = {
redirect: getEnvVar('LOGIN_GITLAB_URL_REDIRECT', ''),
token : getEnvVar('LOGIN_GITLAB_URL_TOKEN', '')
};
this.assignment = {
filename : getEnvVar('ASSIGNMENT_FILENAME', ''),
neededFiles: JSON.parse(getEnvVar('EXERCISE_NEEDED_FILES', '[]')),
name : process.env.DOJO_ASSIGNMENT_NAME || '',
secret : process.env.DOJO_ASSIGNMENT_SECRET || ''
};
this.dockerCompose = {
projectName: process.env.DOCKER_COMPOSE_PROJECT_NAME || ''
projectName: getEnvVar('DOCKER_COMPOSE_PROJECT_NAME', '')
};
this.exerciseResultsFolderMaxSizeInBytes = Number(process.env.EXERCISE_RESULTS_FOLDER_MAX_SIZE_IN_BYTES || 0);
this.exerciseResultsFolderMaxSizeInBytes = Number(getEnvVar('EXERCISE_RESULTS_FOLDER_MAX_SIZE_IN_BYTES', '0'));
this.filenames = {
results: process.env.EXERCISE_RESULTS_FILENAME || ''
results: getEnvVar('EXERCISE_RESULTS_FILENAME', '')
};
}
}
......
This diff is collapsed.
import chalk from 'chalk';
import boxen from 'boxen';
import Icon from '../../../shared/types/Icon';
import AssignmentValidator from './AssignmentValidator';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import chalk from 'chalk';
import boxen from 'boxen';
import Icon from '../../../shared/types/Icon';
import AssignmentValidator from './AssignmentValidator';
import Assignment from '../../models/Assignment';
import axios from 'axios';
import DojoBackendResponse from '../../../shared/types/Dojo/DojoBackendResponse';
import ApiRoute from '../../types/Dojo/ApiRoute';
import ClientsSharedConfig from '../../config/ClientsSharedConfig';
class ClientsSharedAssignmentHelper {
displayExecutionResults(validator: AssignmentValidator, successMessage: string, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }) {
const finalLogGlobalResult = `${ Style.INFO('Global result') } : ${ validator.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`) }`;
const globalResult = validator.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`);
const finalLogGlobalResult = `${ Style.INFO('Global result') } : ${ globalResult }`;
const finalLogSuccessMessage = validator.success ? `${ successMessage }` : '';
const finalLogErrorMessage = !validator.success ? `${ Style.INFO('Error message') } :\n${ Style.FAILURE(validator.fatalErrorMessage) }` : '';
......@@ -21,6 +29,34 @@ class ClientsSharedAssignmentHelper {
textAlignment : 'left'
}));
}
private async getAssignment(nameOrUrl: string): Promise<Assignment | undefined> {
try {
return (await axios.get<DojoBackendResponse<Assignment>>(`${ ClientsSharedConfig.apiURL }${ ApiRoute.ASSIGNMENT_GET }`.replace('{{nameOrUrl}}', encodeURIComponent(nameOrUrl)))).data.data;
} catch ( error ) {
console.log(error);
return undefined;
}
}
private async extractOriginUrl(content: string): Promise<string> {
const regexp = /\[remote "origin"]\r?\n\s*url\s*=\s*(.*)\s*\n/gm;
return Array.from(content.matchAll(regexp), m => m[1])[0];
}
async getAssignmentByName(name: string): Promise<Assignment | undefined> {
return await this.getAssignment(name);
}
async getAssignmentFromPath(path: string): Promise<Assignment | undefined> {
const fullPath = join(path, "./.git/config");
if (!existsSync(fullPath)) {
return undefined;
}
const content = readFileSync(fullPath, 'utf-8');
const url = await this.extractOriginUrl(content);
return await this.getAssignment(url);
}
}
......
import ExerciseResultsFile from '../../../shared/types/Dojo/ExerciseResultsFile';
import ExerciseResultsFile from '../../../shared/types/Dojo/ExerciseResultsFile.js';
import chalk from 'chalk';
import boxen from 'boxen';
import Icon from '../../../shared/types/Icon';
import Icon from '../../../shared/types/Icon.js';
class ClientsSharedExerciseHelper {
displayExecutionResults(exerciseResults: ExerciseResultsFile, containerExitCode: number, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }, additionalText: string = '') {
const finalLogGlobalResult = `${ Style.INFO('Global result: ') }${ exerciseResults.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`) }`;
const finalLogExecutionExitCode = `${ Style.INFO('Execution exit code: ') }${ (containerExitCode == 0 ? Style.SUCCESS : Style.FAILURE)(containerExitCode) }`;
private getOtherInformations(exerciseResults: ExerciseResultsFile, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }) {
return exerciseResults.otherInformations ? [ '', ...exerciseResults.otherInformations.map(information => {
const informationTitle = Style.INFO(`${ information.icon && information.icon !== '' ? Icon[information.icon] + ' ' : '' }${ information.name }: `);
const informationItems = typeof information.itemsOrInformations == 'string' ? information.itemsOrInformations : information.itemsOrInformations.map(item => `- ${ item }`).join('\n');
const finalLogResultNumbers = exerciseResults.successfulTests || exerciseResults.failedTests ? `\n\n${ Style.INFO(Style.SUCCESS('Tests passed: ')) }${ exerciseResults.successfulTests ?? '--' }\n${ Style.INFO(Style.FAILURE('Tests failed: ')) }${ exerciseResults.failedTests ?? '--' }` : '';
return `${ informationTitle }\n${ informationItems }`;
}) ].join('\n\n') : '';
}
const finalLogSuccessResultDetails = (exerciseResults.successfulTestsList ?? []).map(testName => `- ${ Icon.SUCCESS } ${ testName }`).join('\n');
const finalLogFailedResultDetails = (exerciseResults.failedTestsList ?? []).map(testName => `- ${ Icon.FAILURE } ${ testName }`).join('\n');
const finalLogResultDetails = exerciseResults.successfulTestsList || exerciseResults.failedTestsList ? `\n\n${ Style.INFO('Tests: ') }${ finalLogSuccessResultDetails != '' ? '\n' + finalLogSuccessResultDetails : '' }${ finalLogFailedResultDetails != '' ? '\n' + finalLogFailedResultDetails : '' }` : '';
displayExecutionResults(exerciseResults: ExerciseResultsFile, containerExitCode: number, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }, additionalText: string = '') {
const globalResult = exerciseResults.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`);
const finalLogGlobalResult = `${ Style.INFO('Global result: ') }${ globalResult }`;
const finalLogExecutionExitCode = `${ Style.INFO('Execution exit code: ') }${ (containerExitCode === 0 ? Style.SUCCESS : Style.FAILURE)(containerExitCode) }`;
let finalLogInformations = '';
if ( exerciseResults.otherInformations ) {
finalLogInformations = [ '', ...exerciseResults.otherInformations.map(information => {
const informationTitle = Style.INFO(`${ information.icon && information.icon != '' ? Icon[information.icon] + ' ' : '' }${ information.name }: `);
const informationItems = typeof information.itemsOrInformations == 'string' ? information.itemsOrInformations : information.itemsOrInformations.map(item => `- ${ item }`).join('\n');
const finalLogResultNumbers = exerciseResults.successfulTests || exerciseResults.failedTests ? `\n\n${ Style.INFO(Style.SUCCESS('Tests passed: ')) }${ exerciseResults.successfulTests ?? '--' }\n${ Style.INFO(Style.FAILURE('Tests failed: ')) }${ exerciseResults.failedTests ?? '--' }` : '';
return `${ informationTitle }\n${ informationItems }`;
}) ].join('\n\n');
}
let finalLogSuccessResultDetails = (exerciseResults.successfulTestsList ?? []).map(testName => `- ${ Icon.SUCCESS } ${ testName }`).join('\n');
finalLogSuccessResultDetails = finalLogSuccessResultDetails !== '' ? '\n' + finalLogSuccessResultDetails : '';
let finalLogFailedResultDetails = (exerciseResults.failedTestsList ?? []).map(testName => `- ${ Icon.FAILURE } ${ testName }`).join('\n');
finalLogFailedResultDetails = finalLogFailedResultDetails !== '' ? '\n' + finalLogFailedResultDetails : '';
const finalLogResultDetails = exerciseResults.successfulTestsList || exerciseResults.failedTestsList ? `\n\n${ Style.INFO('Tests: ') }${ finalLogSuccessResultDetails }${ finalLogFailedResultDetails }` : '';
const finalLogInformations = this.getOtherInformations(exerciseResults, Style);
console.log(boxen(`${ finalLogGlobalResult }\n\n${ finalLogExecutionExitCode }${ finalLogResultNumbers }${ finalLogResultDetails }${ finalLogInformations }${ additionalText }`, {
title : 'Results',
......
import ApiRoute from '../../types/Dojo/ApiRoute.js';
import ClientsSharedConfig from '../../config/ClientsSharedConfig';
class DojoBackendHelper {
public getApiUrl(route: ApiRoute, options?: Partial<{ userId: number, assignmentNameOrUrl: string, exerciseIdOrUrl: string, gitlabProjectId: string, tagName: string }>): string {
let url = `${ ClientsSharedConfig.apiURL }${ route }`;
if ( options ) {
if ( options.userId ) {
url = url.replace('{{userId}}', encodeURIComponent(options.userId.toString()));
}
if ( options.assignmentNameOrUrl ) {
url = url.replace('{{assignmentNameOrUrl}}', encodeURIComponent(options.assignmentNameOrUrl));
}
if ( options.exerciseIdOrUrl ) {
url = url.replace('{{exerciseIdOrUrl}}', encodeURIComponent(options.exerciseIdOrUrl));
}
if ( options.gitlabProjectId ) {
url = url.replace('{{gitlabProjectId}}', encodeURIComponent(options.gitlabProjectId));
}
if ( options.tagName ) {
url = url.replace('{{tagName}}', encodeURIComponent(options.tagName));
}
}
return url;
}
}
export default new DojoBackendHelper();
\ No newline at end of file
import AssignmentFile from '../../../shared/types/Dojo/AssignmentFile';
import AssignmentFile from '../../../shared/types/Dojo/AssignmentFile.js';
import { TypedEmitter } from 'tiny-typed-emitter';
import ExerciseRunningEvents from '../../types/Dojo/ExerciseRunningEvents';
import ExerciseRunningEvents from '../../types/Dojo/ExerciseRunningEvents.js';
import { spawn } from 'child_process';
import ExerciseCheckerError from '../../../shared/types/Dojo/ExerciseCheckerError';
import ExerciseCheckerError from '../../../shared/types/Dojo/ExerciseCheckerError.js';
import { ChildProcessWithoutNullStreams } from 'node:child_process';
......@@ -18,7 +18,17 @@ class ExerciseDockerCompose {
private currentStep: string = 'NOT_RUNNING';
constructor(private projectName: string, private assignmentFile: AssignmentFile, private executionFolder: string, private composeFileOverride: Array<string> = []) {
private readonly projectName: string;
private readonly assignmentFile: AssignmentFile;
private readonly executionFolder: string;
private readonly composeFileOverride: Array<string> = [];
constructor(projectName: string, assignmentFile: AssignmentFile, executionFolder: string, composeFileOverride: Array<string> = []) {
this.projectName = projectName;
this.assignmentFile = assignmentFile;
this.executionFolder = executionFolder;
this.composeFileOverride = composeFileOverride;
this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
this.allLogs += log;
this.displayableLogs += displayable ? log : '';
......@@ -49,15 +59,17 @@ class ExerciseDockerCompose {
}
private registerChildProcess(childProcess: ChildProcessWithoutNullStreams, resolve: (value: (number | PromiseLike<number>)) => void, reject: (reason?: unknown) => void, displayable: boolean, rejectIfCodeIsNotZero: boolean) {
childProcess.stdout.on('data', (data) => {
childProcess.stdout.on('data', data => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-call
this.log(data.toString(), false, displayable);
});
childProcess.stderr.on('data', (data) => {
childProcess.stderr.on('data', data => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-call
this.log(data.toString(), true, displayable);
});
childProcess.on('exit', (code) => {
childProcess.on('exit', code => {
code === null || (rejectIfCodeIsNotZero && code !== 0) ? reject(code) : resolve(code);
});
}
......@@ -66,7 +78,8 @@ class ExerciseDockerCompose {
(async () => {
let containerExitCode: number = -1;
const dockerComposeCommand = `docker compose --project-name ${ this.projectName } --progress plain --file docker-compose.yml ${ this.composeFileOverride.map((file) => `--file "${ file }"`).join(' ') }`;
const filesOverrideArguments = this.composeFileOverride.map(file => `--file "${ file }"`).join(' ');
const dockerComposeCommand = `docker compose --project-name ${ this.projectName } --progress plain --file docker-compose.yml ${ filesOverrideArguments }`;
// Run the service
{
......
import { TypedEmitter } from 'tiny-typed-emitter';
import ExerciseRunningEvents from '../../types/Dojo/ExerciseRunningEvents';
import ExerciseCheckerError from '../../../shared/types/Dojo/ExerciseCheckerError';
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';
import Toolbox from '../../../shared/helpers/Toolbox';
import Toolbox from '../../../shared/helpers/Toolbox.js';
import * as fs from 'fs-extra';
import ExerciseResultsFile from '../../../shared/types/Dojo/ExerciseResultsFile';
import ExerciseResultsFile from '../../../shared/types/Dojo/ExerciseResultsFile.js';
import JSON5 from 'json5';
import Json5FileValidator from '../../../shared/helpers/Json5FileValidator';
import Json5FileValidator from '../../../shared/helpers/Json5FileValidator.js';
import ClientsSharedConfig from '../../config/ClientsSharedConfig';
class ExerciseResultsSanitizerAndValidator {
......@@ -17,9 +17,17 @@ class ExerciseResultsSanitizerAndValidator {
private resultsFilePath: string = '';
constructor(private folderResultsDojo: string, private folderResultsExercise: string, private containerExitCode: number) { }
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 async resultsFileSanitization() {
private resultsFileSanitization() {
this.events.emit('step', 'RESULTS_FILE_SANITIZATION', 'Sanitizing results file');
if ( this.exerciseResults.success === undefined ) {
......@@ -33,11 +41,11 @@ class ExerciseResultsSanitizerAndValidator {
this.events.emit('endStep', 'RESULTS_FILE_SANITIZATION', 'Results file sanitized', false);
}
private async resultsFileProvided(path: string): Promise<boolean> {
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, path);
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);
......@@ -49,7 +57,7 @@ class ExerciseResultsSanitizerAndValidator {
// Results file content sanitization
await this.resultsFileSanitization();
this.resultsFileSanitization();
// Results folder size
......@@ -68,11 +76,6 @@ class ExerciseResultsSanitizerAndValidator {
return true;
}
private async resultsFileNotProvided(): Promise<boolean> {
await this.resultsFileSanitization();
return true;
}
run() {
(async () => {
// Results file existence
......@@ -80,7 +83,7 @@ class ExerciseResultsSanitizerAndValidator {
const resultsFileOriginPath = path.join(this.folderResultsExercise, ClientsSharedConfig.filenames.results);
this.resultsFilePath = path.join(this.folderResultsDojo, ClientsSharedConfig.filenames.results);
let result: boolean;
let result: boolean = true;
if ( fs.existsSync(resultsFileOriginPath) ) {
this.events.emit('endStep', 'CHECK_RESULTS_FILE_EXIST', 'Results file found', false);
......@@ -94,7 +97,7 @@ class ExerciseResultsSanitizerAndValidator {
} else {
this.events.emit('endStep', 'CHECK_RESULTS_FILE_EXIST', 'Results file not found', false);
result = await this.resultsFileNotProvided();
this.resultsFileSanitization();
}
if ( result ) {
......
import { spawnSync } from 'node:child_process';
import { Language } from '../../models/Assignment';
import SharedConfig from '../../../shared/config/SharedConfig';
const IMAGE_NAME = 'dojo-sonar-scanner'
const OUT_DIR = 'bw-output';
class SonarAnalyzer {
buildDocker = () => {
const buildProcess = spawnSync('docker', ['build', '--tag', IMAGE_NAME, '/sonar']);
if ( buildProcess.status !== 0 ) {
console.log(buildProcess.stdout.toString())
console.log(buildProcess.stderr.toString())
return false;
}
return true;
}
mustRunBuild = (language: Language, buildLine: string | undefined) => {
return [Language.c, Language.cpp, Language.objc].includes(language) && buildLine != undefined;
}
runBuildStep = (buildLine: string) => {
const process = spawnSync(`docker run -v ./:/usr/src ${IMAGE_NAME} /usr/local/bin/build-wrapper-linux-x86-64 --out-dir ${OUT_DIR} ` + buildLine, [], { shell: true })
if ( process.status !== 0 ) {
console.log(process.stdout.toString())
console.log(process.stderr.toString())
return false;
}
return true;
}
runAnalysis = (sonarKey: string, language: Language, buildLine: string | undefined) => {
let addParam: string[] = [];
if (this.mustRunBuild(language, buildLine)) {
addParam = [ `-Dsonar.cfamily.build-wrapper-output=/usr/src/${OUT_DIR}`];
}
const process = spawnSync(
'docker',
['run', '-v', './:/usr/src',
IMAGE_NAME , 'sonar-scanner',
'-Dsonar.qualitygate.wait=true',
'-Dsonar.projectKey=' + sonarKey,
'-Dsonar.sources=.',
'-Dsonar.host.url=' + SharedConfig.sonar.url,
'-Dsonar.login=' + SharedConfig.sonar.token,
...addParam])
return process.status === 0;
}
}
export default new SonarAnalyzer();
\ No newline at end of file
import GitlabRepository from '../../shared/types/Gitlab/GitlabRepository';
import User from './User';
import Exercise from './Exercise';
import User from './User.js';
import Exercise from './Exercise.js';
import * as Gitlab from '@gitbeaker/rest';
import SonarProjectCreation from '../../shared/types/Sonar/SonarProjectCreation';
interface Assignment {
name: string;
gitlabId: number;
gitlabLink: string;
gitlabCreationInfo: GitlabRepository;
gitlabLastInfo: GitlabRepository;
gitlabCreationInfo: Gitlab.ProjectSchema;
gitlabLastInfo: Gitlab.ProjectSchema;
gitlabLastInfoDate: string;
published: boolean;
useSonar: boolean;
allowSonarFailure: boolean;
sonarKey: string;
sonarCreationInfo: SonarProjectCreation;
language: Language;
staff: Array<User>;
exercises: Array<Exercise>;
......@@ -19,4 +25,74 @@ interface Assignment {
}
export default Assignment;
\ No newline at end of file
export enum Language {
abap = "abap",
ada = "ada",
asm = "asm",
bash = "bash",
bqn = "bqn",
c = "c",
caml = "caml",
cloudformation = "cloudformation",
cpp = "cpp",
csharp = "csharp",
css = "css",
cuda = "cuda",
dart = "dart",
delphi = "delphi",
docker = "docker",
erlang = "erlang",
f = "f",
fsharp = "fsharp",
flex = "flex",
fortran = "fortran",
futhark = "futhark",
go = "go",
groovy = "groovy",
haskell = "haskell",
hepial = "hepial",
json = "json",
jsp = "jsp",
java = "java",
js = "js",
julia = "julia",
kotlin = "kotlin",
kubernetes = "kubernetes",
latex = "latex",
lisp = "lisp",
lua = "lua",
matlab = "matlab",
objc = "objc",
ocaml = "ocaml",
pascal = "pascal",
pearl = "pearl",
perl = "perl",
php = "php",
postscript = "postscript",
powershell = "powershell",
prolog = "prolog",
promela = "promela",
python = "python",
r = "r",
ruby = "ruby",
rust = "rust",
scala = "scala",
sql = "sql",
smalltalk = "smalltalk",
swift = "swift",
terraform = "terraform",
text = "text",
ts = "ts",
tsql = "tsql",
typst = "typst",
vba = "vba",
vbnet = "vbnet",
web = "web",
xml = "xml",
yaml = "yaml",
other = "other"
}
export default Assignment;
import GitlabRepository from '../../shared/types/Gitlab/GitlabRepository';
import { CommitSchema } from '@gitbeaker/rest';
import User from './User';
import Assignment from './Assignment';
import User from './User.js';
import Assignment from './Assignment.js';
import * as Gitlab from '@gitbeaker/rest';
import SonarProjectCreation from '../../shared/types/Sonar/SonarProjectCreation';
interface Exercise {
......@@ -10,15 +10,19 @@ interface Exercise {
name: string;
gitlabId: number;
gitlabLink: string;
gitlabCreationInfo: GitlabRepository;
gitlabLastInfo: GitlabRepository;
gitlabCreationInfo: Gitlab.ProjectSchema;
gitlabLastInfo: Gitlab.ProjectSchema;
gitlabLastInfoDate: string;
sonarKey: string;
sonarCreationInfo: SonarProjectCreation;
members: Array<User> | undefined;
assignment: Assignment | undefined;
isCorrection: boolean;
correctionCommit: CommitSchema | undefined;
correctionCommit: Gitlab.CommitSchema | undefined;
correctionDescription: string | undefined;
}
......
import AssignmentFile from '../../shared/types/Dojo/AssignmentFile';
import Assignment from './Assignment';
import GitlabFile from '../../shared/types/Gitlab/GitlabFile';
import AssignmentFile from '../../shared/types/Dojo/AssignmentFile.js';
import Assignment from './Assignment.js';
import * as Gitlab from '@gitbeaker/rest';
interface ExerciseAssignment {
assignment: Assignment;
assignmentFile: AssignmentFile;
immutable: Array<GitlabFile>;
immutable: Array<Gitlab.RepositoryFileSchema>;
}
......
import { CommitSchema } from '@gitbeaker/rest';
import ExerciseResultsFile from '../../shared/types/Dojo/ExerciseResultsFile';
import { IFileDirStat } from '../../shared/helpers/recursiveFilesStats/RecursiveFilesStats';
export interface Result {
exerciseId: string;
dateTime: string;
commit: CommitSchema;
exitCode: number;
files: Array<IFileDirStat>;
results: ExerciseResultsFile;
success: boolean;
}
export default Result;
\ No newline at end of file
import { CommitSchema } from '@gitbeaker/rest';
import Assignment from './Assignment';
import Exercise from './Exercise';
interface Tags {
name: string;
type: 'Language' | 'Framework' | 'Theme' | 'UserDefined';
exercise: Exercise | undefined;
assignment: Assignment | undefined;
correctionCommit: CommitSchema | undefined;
}
export default Tags;
\ No newline at end of file
interface TagProposal {
name: string;
type: 'Language' | 'Framework' | 'Theme' | 'UserDefined';
state: 'PendingApproval' | 'Declined' | 'Approved';
details: string;
}
export default TagProposal;
import UserRole from './UserRole';
import Exercise from './Exercise';
import Assignment from './Assignment';
import GitlabProfile from '../../shared/types/Gitlab/GitlabProfile';
import UserRole from './UserRole.js';
import Exercise from './Exercise.js';
import Assignment from './Assignment.js';
import * as Gitlab from '@gitbeaker/rest';
interface User {
......@@ -10,7 +10,7 @@ interface User {
mail: string;
role: UserRole;
gitlabUsername: string;
gitlabLastInfo: GitlabProfile;
gitlabLastInfo: Gitlab.ExpandedUserSchema;
isTeachingStaff: boolean;
isAdmin: boolean;
deleted: boolean;
......
enum ApiRoute {
LOGIN = '/login',
REFRESH_TOKENS = '/refresh_tokens',
TEST_SESSION = '/test_session',
GITLAB_CHECK_TEMPLATE_ACCESS = '/gitlab/project/{{id}}/checkTemplateAccess',
ASSIGNMENT_GET = '/assignments/{{nameOrUrl}}',
ASSIGNMENT_CREATE = '/assignments',
ASSIGNMENT_PUBLISH = '/assignments/{{nameOrUrl}}/publish',
ASSIGNMENT_UNPUBLISH = '/assignments/{{nameOrUrl}}/unpublish',
ASSIGNMENT_CORRECTION_LINK = '/assignments/{{assignmentNameOrUrl}}/corrections',
ASSIGNMENT_CORRECTION_UPDATE = '/assignments/{{assignmentNameOrUrl}}/corrections/{{exerciseIdOrUrl}}',
EXERCISE_CREATE = '/assignments/{{nameOrUrl}}/exercises',
EXERCISE_ASSIGNMENT = '/exercises/{{id}}/assignment',
EXERCISE_RESULTS = '/exercises/{{id}}/results'
CLIENTS_CONFIG = '/clients_config',
LOGIN = '/login',
REFRESH_TOKENS = '/refresh_tokens',
TEST_SESSION = '/test_session',
GITLAB_CHECK_TEMPLATE_ACCESS = '/gitlab/project/{{gitlabProjectId}}/checkTemplateAccess',
ASSIGNMENT_GET = '/assignments/{{assignmentNameOrUrl}}',
ASSIGNMENT_CREATE = '/assignments',
ASSIGNMENT_PUBLISH = '/assignments/{{assignmentNameOrUrl}}/publish',
ASSIGNMENT_UNPUBLISH = '/assignments/{{assignmentNameOrUrl}}/unpublish',
ASSIGNMENT_CORRECTION_LINK = '/assignments/{{assignmentNameOrUrl}}/corrections',
ASSIGNMENT_CORRECTION_UPDATE_DELETE = '/assignments/{{assignmentNameOrUrl}}/corrections/{{exerciseIdOrUrl}}',
EXERCISE_ASSIGNMENT = '/exercises/{{exerciseIdOrUrl}}/assignment',
EXERCISE_CREATE = '/assignments/{{assignmentNameOrUrl}}/exercises',
EXERCISE_RESULTS = '/exercises/{{exerciseIdOrUrl}}/results',
EXERCISE_LIST = '/exercises',
EXERCISE_GET_DELETE = '/exercises/{{exerciseIdOrUrl}}',
EXERCISE_DETAILS_GET = '/exercises/{{exerciseIdOrUrl}}/details',
EXERCISE_MEMBERS_GET = '/exercises/{{exerciseIdOrUrl}}/members',
USER_EXERCISES_LIST = '/users/{{userId}}/exercises',
USER_LIST = '/users',
TAG_CREATE = '/tags',
TAG_DELETE = '/tags/{{tagName}}',
TAG_PROPOSAL_GET_CREATE = '/tags/proposals',
TAG_PROPOSAL_UPDATE = '/tags/proposals/{{tagName}}',
SONAR = '/sonar',
SONAR_QUALITIES = '/sonar/testqualities',
LANGUAGES = '/assignments/languages',
}
......