diff --git a/.gitmodules b/.gitmodules index f612bc05fef311547b546e68da614a7aec463793..139e244614dce96d58e362ede9d99239d56c2271 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = ../../shared/jetbrains_configuration.git [submodule "NodeApp/src/shared"] path = NodeApp/src/shared - url = ../../shared/nodesharedcode.git + url = https://gitedu.hesge.ch/dojo_project/projects/shared/nodesharedcode [submodule "NodeApp/src/sharedByClients"] path = NodeApp/src/sharedByClients - url = ../../shared/nodeclientsharedcode.git + url = https://gitedu.hesge.ch/dojo_project/projects/shared/nodeclientsharedcode \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b58b603fea78041071d125a30db58d79b3d49217 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/dojocli.iml b/.idea/dojocli.iml new file mode 100644 index 0000000000000000000000000000000000000000..24643cc37449b4bde54411a80b8ed61258225e34 --- /dev/null +++ b/.idea/dojocli.iml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.tmp" /> + <excludeFolder url="file://$MODULE_DIR$/temp" /> + <excludeFolder url="file://$MODULE_DIR$/tmp" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ab30517f783babc3ee1b319137f7aef281d02af --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/dojocli.iml" filepath="$PROJECT_DIR$/.idea/dojocli.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/NodeApp/package-lock.json b/NodeApp/package-lock.json index 2ae9954cb1e3dbd04653ce0d2d34f80e8e23fdf8..c551479010658b9f445052141b92a6ec3ce1ee36 100644 --- a/NodeApp/package-lock.json +++ b/NodeApp/package-lock.json @@ -22,6 +22,7 @@ "commander": "^12.1.0", "form-data": "^4.0.0", "fs-extra": "^11.2.0", + "fuse.js": "^7.0.0", "http-status-codes": "^2.3.0", "inquirer": "^8.2.6", "json5": "^2.2.3", @@ -3552,6 +3553,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuse.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", + "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", + "engines": { + "node": ">=10" + } + }, "node_modules/genversion": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/genversion/-/genversion-3.2.0.tgz", diff --git a/NodeApp/package.json b/NodeApp/package.json index 5cc93c6a724a90acd4c5a5c2b643ed7a8494694e..f7692dda490c057f5660c08dd83ee4861d017106 100644 --- a/NodeApp/package.json +++ b/NodeApp/package.json @@ -8,9 +8,9 @@ "bin" : { "dojo": "./dist/app.js" }, - "pkg" : { + "pkg": { "scripts": [], - "assets" : [ + "assets": [ "node_modules/axios/dist/node/axios.cjs", ".env", "assets/**/*" diff --git a/NodeApp/src/commander/exercise/ExerciseCommand.ts b/NodeApp/src/commander/exercise/ExerciseCommand.ts index 8089092754831b94bf8b9fba3e07e2fcd92a657e..02e40e36a7bd369b056c2cf5a8303e796d8af5f5 100644 --- a/NodeApp/src/commander/exercise/ExerciseCommand.ts +++ b/NodeApp/src/commander/exercise/ExerciseCommand.ts @@ -2,6 +2,7 @@ import CommanderCommand from '../CommanderCommand.js'; import ExerciseCreateCommand from './subcommands/ExerciseCreateCommand.js'; import ExerciseRunCommand from './subcommands/ExerciseRunCommand.js'; import ExerciseCorrectionCommand from './subcommands/ExerciseCorrectionCommand.js'; +import ExerciseDeleteCommand from './subcommands/ExerciseDeleteCommand'; class ExerciseCommand extends CommanderCommand { @@ -15,6 +16,7 @@ class ExerciseCommand extends CommanderCommand { protected defineSubCommands() { ExerciseCreateCommand.registerOnCommand(this.command); ExerciseRunCommand.registerOnCommand(this.command); + ExerciseDeleteCommand.registerOnCommand(this.command); ExerciseCorrectionCommand.registerOnCommand(this.command); } diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts index 0a7aac7bb2302c1b1ec538803f4e87c1fb4d567e..650d1504f725c7db60d9cb5b45140838d21d5300 100644 --- a/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts +++ b/NodeApp/src/commander/exercise/subcommands/ExerciseCorrectionCommand.ts @@ -17,8 +17,8 @@ class ExerciseCorrectionCommand extends CommanderCommand { protected defineCommand() { this.command - .description('link an exercise repo as a correction for an assignment') - .requiredOption('-a, --assignment <string>', 'id or url of the assignment of the correction') + .description('list corrections of an assignment') + .requiredOption('-a, --assignment <string>', 'id or url of the assignment') .action(this.commandAction.bind(this)); } diff --git a/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts b/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ec20cdfd13b93306de1bf9095adc9c6d40abb5c --- /dev/null +++ b/NodeApp/src/commander/exercise/subcommands/ExerciseDeleteCommand.ts @@ -0,0 +1,38 @@ +import CommanderCommand from '../../CommanderCommand'; +import DojoBackendManager from '../../../managers/DojoBackendManager'; +import AccessesHelper from '../../../helpers/AccessesHelper'; +import TextStyle from '../../../types/TextStyle'; + + +class ExerciseDeleteCommand extends CommanderCommand { + protected commandName: string = 'delete'; + + protected defineCommand(): void { + this.command + .description('delete an exercise') + .argument('id or url', 'id or url of the exercise') + .action(this.commandAction.bind(this)); + } + + private async dataRetrieval() { + console.log(TextStyle.BLOCK('Please wait while we verify and retrieve data...')); + + await AccessesHelper.checkStudent(); + } + + private async deleteExercise(exerciseIdOrUrl: string) { + console.log(TextStyle.BLOCK('Please wait while we are deleting the exercise...')); + + await DojoBackendManager.deleteExercise(exerciseIdOrUrl); + } + + protected async commandAction(exerciseIdOrUrl: string): Promise<void> { + try { + await this.dataRetrieval(); + await this.deleteExercise(exerciseIdOrUrl); + } catch ( e ) { /* Do nothing */ } + } +} + + +export default new ExerciseDeleteCommand(); diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts index 414945f897192e5422b9cf10088403e67c5e554b..0a878367581dbd7022567590557cf88222c05d00 100644 --- a/NodeApp/src/managers/DojoBackendManager.ts +++ b/NodeApp/src/managers/DojoBackendManager.ts @@ -13,6 +13,7 @@ import DojoBackendHelper from '../sharedByClients/helpers/Dojo/DojoBackendHe import GitlabPipelineStatus from '../shared/types/Gitlab/GitlabPipelineStatus.js'; import Tag from '../sharedByClients/models/Tag'; import TagProposal from '../sharedByClients/models/TagProposal'; +import Result from '../sharedByClients/models/Result'; class DojoBackendManager { @@ -84,7 +85,6 @@ class DojoBackendManager { } } - public async login(gitlabTokens: GitlabToken): Promise<User | undefined> { try { return (await axios.post<DojoBackendResponse<User>>(DojoBackendHelper.getApiUrl(ApiRoute.LOGIN), { @@ -365,6 +365,85 @@ class DojoBackendManager { return false; } } + + public async getUserExercises(): Promise<Array<Exercise> | undefined> { + try { + const response = await axios.get<DojoBackendResponse<Array<Exercise>>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_LIST)); + return response.data.data; + } catch ( error ) { + console.error('Error fetching user exercises:', error); + return undefined; + } + } + + public async getExerciseDetails(exerciseIdOrUrl: string): Promise<Exercise | undefined> { + try { + + const response = await axios.get<Exercise>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_DETAILS_GET, { + exerciseIdOrUrl: exerciseIdOrUrl + })); + return response.data; + } catch ( error ) { + console.error('Error fetching exercise details:', error); + return undefined; + } + } + + public async deleteExercise(exerciseIdOrUrl: string, verbose: boolean = true): Promise<void> { + const spinner: ora.Ora = ora('Deleting exercise...'); + + if ( verbose ) { + spinner.start(); + } + + try { + await axios.delete<DojoBackendResponse<Exercise>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_GET_DELETE, { + exerciseIdOrUrl: exerciseIdOrUrl + })); + + if ( verbose ) { + spinner.succeed(`Exercise deleted with success`); + } + } catch ( error ) { + this.handleApiError(error, spinner, verbose, `Exercise deleting error: ${ error }`); + + throw error; + } + } + + public async getExerciseMembers(exerciseIdOrUrl: string): Promise<Array<User>> { + return (await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_MEMBERS_GET, { + exerciseIdOrUrl: exerciseIdOrUrl + }))).data.data; + } + + public async getExerciseResults(exerciseIdOrUrl: string): Promise<Array<Result>> { + try { + const response = await axios.get(DojoBackendHelper.getApiUrl(ApiRoute.EXERCISE_RESULTS, { + exerciseIdOrUrl: exerciseIdOrUrl + })); + + return response.data as Array<Result>; + } catch ( error ) { + console.error('Error fetching exercise results:', error); + return []; + } + } + + public async getUsers(roleFilter?: string): Promise<Array<User> | undefined> { + try { + const response = await axios.get<DojoBackendResponse<Array<User>>>(DojoBackendHelper.getApiUrl(ApiRoute.USER_LIST), { params: roleFilter ? { roleFilter: roleFilter } : {} }); + + return response.data.data; + } catch ( error ) { + console.error('Error fetching professors:', error); + return undefined; + } + } + + public async getTeachers(): Promise<Array<User> | undefined> { + return this.getUsers('teacher'); + } } diff --git a/NodeApp/src/sharedByClients b/NodeApp/src/sharedByClients index 8c87edbbc5734d6b85d1e52063e8d815807277c0..6e46ceeabffc5ba6fdd27ca89b9b5f82c68f82de 160000 --- a/NodeApp/src/sharedByClients +++ b/NodeApp/src/sharedByClients @@ -1 +1 @@ -Subproject commit 8c87edbbc5734d6b85d1e52063e8d815807277c0 +Subproject commit 6e46ceeabffc5ba6fdd27ca89b9b5f82c68f82de