diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts index 0a878367581dbd7022567590557cf88222c05d00..0317027ac5b3c14cc07dc4b1486c310734657aad 100644 --- a/NodeApp/src/managers/DojoBackendManager.ts +++ b/NodeApp/src/managers/DojoBackendManager.ts @@ -1,7 +1,6 @@ import axios, { AxiosError } from 'axios'; import ora from 'ora'; import ApiRoute from '../sharedByClients/types/Dojo/ApiRoute.js'; -import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig.js'; import Assignment from '../sharedByClients/models/Assignment.js'; import DojoBackendResponse from '../shared/types/Dojo/DojoBackendResponse.js'; import Exercise from '../sharedByClients/models/Exercise.js'; @@ -14,10 +13,121 @@ import GitlabPipelineStatus from '../shared/types/Gitlab/GitlabPipelineStatus.j import Tag from '../sharedByClients/models/Tag'; import TagProposal from '../sharedByClients/models/TagProposal'; import Result from '../sharedByClients/models/Result'; +import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig'; +import inquirer from 'inquirer'; +import SharedConfig from '../shared/config/SharedConfig'; +import ConfigFiles from '../config/ConfigFiles'; class DojoBackendManager { - private handleApiError(error: unknown, spinner: ora.Ora, verbose: boolean, defaultErrorMessage?: string, otherErrorHandler?: (error: AxiosError, spinner: ora.Ora, verbose: boolean) => void) { + readonly API_URL_CONFIG_KEY = 'apiUrl'; + + public async getApiUrl(): Promise<string> { + //Read the config file to get the api url. If there is no api url, then ask the user to provide one. + return ConfigFiles.stateConfigFile.getParam(this.API_URL_CONFIG_KEY) as string | null || await this.askApiUrl(); + } + + public async cleanApiUrl(): Promise<void> { + const cleanApiSpinner: ora.Ora = ora('Cleaning Api URL...').start(); + ConfigFiles.stateConfigFile.setParam(this.API_URL_CONFIG_KEY, null); + cleanApiSpinner.succeed('API URL successfully cleaned'); + } + + /** + * Check if the given api url is valid and save it in the config file if it is. + * @param apiUrl + * @returns {Promise<boolean>} true if the api url is valid, false otherwise + */ + public async setApiUrl(apiUrl: string): Promise<boolean> { + let isApiUrlValid: boolean = false; + + const testApiSpinner: ora.Ora = ora('Testing Api URL...').start(); + try { + isApiUrlValid = (await axios.get<DojoBackendResponse<unknown>>(`${ apiUrl }${ ApiRoute.CLIENTS_CONFIG }`)).status === 200; + } catch ( e ) { + isApiUrlValid = false; + } + + if ( isApiUrlValid ) { + // End step: Store the preference in the config file + ConfigFiles.stateConfigFile.setParam(this.API_URL_CONFIG_KEY, apiUrl); + } + + isApiUrlValid ? testApiSpinner.succeed('API URL successfully saved') : testApiSpinner.fail('The API URL is invalid'); + + return isApiUrlValid; + } + + public async askApiUrl(showClean: boolean = false): Promise<string> { + const presets: Array<{ name: string, value: string } | inquirer.Separator> = [ { + name : 'HEPIA', + value: 'https://rdps.hesge.ch/dojo/api' + }, { + name : 'Other', + value: 'other' + } ]; + + if ( !SharedConfig.production ) { + presets.unshift({ + name : 'DEV', + value: 'http://localhost:30993' + }, { + name : 'TEST', + value: 'http://dojo-test.edu.hesge.ch/dojo/api' + }, new inquirer.Separator()); + } + + if ( showClean ) { + presets.push(new inquirer.Separator(), { + name : 'Clean API settings', + value: 'clean' + }); + } + + presets.push(new inquirer.Separator(), { + name : 'Quit', + value: 'quit' + }); + + let apiUrl: string = ''; + let isApiUrlValid: boolean = false; + + do { + // First step: Propose some presets with inquirer + apiUrl = (await inquirer.prompt({ + name : 'apiUrl', + message : 'Which API do you want to use?', + type : 'list', + pageSize: 1000, + choices : presets, + default : SharedConfig.production ? 'other' : 'http://localhost:30993' + })).apiUrl; + + + // Second step: If the user chooses other, then ask for the url of the api + switch ( apiUrl ) { + case 'other': + apiUrl = (await inquirer.prompt({ + name : 'apiUrl', + message: 'Please provide the URL of the API', + type : 'input' + })).apiUrl; + break; + case 'clean': + await this.cleanApiUrl(); + return ''; + case 'quit': + process.exit(0); + } + + // Third step: Test the api url + isApiUrlValid = await this.setApiUrl(apiUrl); + } while ( !isApiUrlValid ); + + return apiUrl; + } + + private async handleApiError(error: unknown, spinner: ora.Ora, verbose: boolean, defaultErrorMessage?: string, otherErrorHandler?: (error: AxiosError, spinner: ora.Ora, verbose: boolean) => void) { const unknownErrorMessage: string = 'unknown error'; if ( verbose ) { @@ -129,7 +239,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Template error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Template error: ${ error }`); return false; } @@ -154,7 +264,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Assignment creation error: unknown error`); + await this.handleApiError(error, spinner, verbose, `Assignment creation error: unknown error`); throw error; } @@ -176,7 +286,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Exercise creation error: unknown error`); + await this.handleApiError(error, spinner, verbose, `Exercise creation error: unknown error`); throw error; } @@ -198,7 +308,7 @@ class DojoBackendManager { return; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Assignment visibility change error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Assignment visibility change error: ${ error }`); throw error; } @@ -230,7 +340,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Correction ${ isUpdate ? 'update' : 'link' } error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Correction ${ isUpdate ? 'update' : 'link' } error: ${ error }`); return false; } @@ -255,7 +365,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Correction unlink error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Correction unlink error: ${ error }`); return false; } @@ -280,7 +390,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag creation error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag creation error: ${ error }`); return undefined; } @@ -302,7 +412,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag deletion error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag deletion error: ${ error }`); return false; } @@ -335,7 +445,7 @@ class DojoBackendManager { return response.data.data; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag proposal creation error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag proposal creation error: ${ error }`); return undefined; } @@ -360,7 +470,7 @@ class DojoBackendManager { return true; } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Tag proposal answer error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Tag proposal answer error: ${ error }`); return false; } @@ -405,7 +515,7 @@ class DojoBackendManager { spinner.succeed(`Exercise deleted with success`); } } catch ( error ) { - this.handleApiError(error, spinner, verbose, `Exercise deleting error: ${ error }`); + await this.handleApiError(error, spinner, verbose, `Exercise deleting error: ${ error }`); throw error; }