diff --git a/deadlock-plugins/deadlock-extension/resources/js/gettingStartedView.js b/deadlock-plugins/deadlock-extension/resources/js/gettingStartedView.js index 363a55f54e7adbcc2c6d4ca27ec46a730a2e4b81..547953b63bc964364cd695380fdad8a1982613d7 100644 --- a/deadlock-plugins/deadlock-extension/resources/js/gettingStartedView.js +++ b/deadlock-plugins/deadlock-extension/resources/js/gettingStartedView.js @@ -1,4 +1,3 @@ -// This file is used by gettingStarted via a <script> tag const vscode = acquireVsCodeApi(); function launchChooseMissionWorkdirAction() { diff --git a/deadlock-plugins/deadlock-extension/resources/styles/gettingStartedView.css b/deadlock-plugins/deadlock-extension/resources/styles/gettingStartedView.css index 5ae4f6fcf2fff5cd84531b5ab0e3f00fa46cf26f..afd0301f87f3b840c5beade48a0660955d2c0e82 100644 --- a/deadlock-plugins/deadlock-extension/resources/styles/gettingStartedView.css +++ b/deadlock-plugins/deadlock-extension/resources/styles/gettingStartedView.css @@ -1,5 +1,3 @@ -/* This file is used by gettingStarted via a <style> tag */ - .deadlock-getting-started-card-container { display: flex; justify-content: center; diff --git a/deadlock-plugins/deadlock-extension/src/core/api.service.ts b/deadlock-plugins/deadlock-extension/src/core/api.service.ts index 2552c328efe68995c4ff495716e89dfd6e7d1b40..630de2ca05ec413ad3e43f12699afb10bc90c784 100644 --- a/deadlock-plugins/deadlock-extension/src/core/api.service.ts +++ b/deadlock-plugins/deadlock-extension/src/core/api.service.ts @@ -51,28 +51,24 @@ export default class ApiService { return res; }, async (err) => { - this.handleResponseError(err); + return await this.handleResponseError(err); }, ); } - private async handleResponseError(err: { - config: AxiosRequestConfig & { _retry: boolean }; - response: { status: number; data: any }; - }) { + private async handleResponseError(err) { const originalConfig = err.config; if (err.response) { // Access Token was expired if (err.response.status === 401) { if (!originalConfig._retry) { originalConfig._retry = true; - return this.onRetry(originalConfig); + return await this.onRetry(originalConfig); } else if (originalConfig._retry) { // IF REFRESH TOKEN NOT WORK, REQUEST NEW CONNECTION IN USER BROWSER - return this.onInvalidRefreshToken(originalConfig); + return await this.onInvalidRefreshToken(originalConfig); } } - if (err.response.status === 403 && err.response.data) { return Promise.reject(err.response.data); } @@ -119,6 +115,8 @@ export default class ApiService { } getUser(): Promise<User> { - return this.axiosInstance.get<User>(`auth`).then((res) => res.data); + return this.axiosInstance.get<User>(`auth`).then((res) => { + return res.data; + }); } } diff --git a/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts b/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts index 9dcdc452f031924a31a33349fd1ce8566ffedee0..bcaea34d75703232e460fd717821db4245b80f54 100644 --- a/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts +++ b/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts @@ -1,12 +1,8 @@ import { commands } from 'vscode'; import { Command } from '../theia/command'; import Controller from './controller'; -import ExtensionStore from './extensionStore'; - export class CommandHandler { - private extensionStore: ExtensionStore; constructor(private controller: Controller) { - this.extensionStore = ExtensionStore.getInstance(); this.initCommandHandler(); } diff --git a/deadlock-plugins/deadlock-extension/src/core/controller.ts b/deadlock-plugins/deadlock-extension/src/core/controller.ts index 951ce840caf90fc0e8f6d5b80a261e80910c581d..7d3d362dff3265d01acf55d5ea49180c11d4453f 100644 --- a/deadlock-plugins/deadlock-extension/src/core/controller.ts +++ b/deadlock-plugins/deadlock-extension/src/core/controller.ts @@ -15,7 +15,6 @@ import ExtensionStore from './extensionStore'; import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection'; import Mission from './mission'; import ApiService from './api.service'; -import KeycloakOAuth2DeviceFlowConnectionVSCodeImpl from './keycloakOAuth2DeviceFlowConnectionVSCodeImpl'; import SshKeyManager from './sshKeyManager'; import { GiteaPublicProperties } from '../model/giteaPublicProperties.model'; import { User } from '../model/user.model'; @@ -34,7 +33,7 @@ export default class Controller { this.briefingView = new BriefingView(); this.quickSetupView = new QuickSetupView(context.extensionUri); this.commandHandler = new CommandHandler(this); - this.connection = new KeycloakOAuth2DeviceFlowConnectionVSCodeImpl( + this.connection = new KeycloakOAuth2DeviceFlowConnection( KEYCLOAK_DEVICE_AUTH_URL, KEYCLOAK_TOKEN_CREATE_URL, KEYCLOAK_USER_INFO_URL, @@ -49,13 +48,20 @@ export default class Controller { const that = this; vscode.window.registerUriHandler({ handleUri(uri: vscode.Uri) { - const queryParams: URLSearchParams = new URLSearchParams(uri.query); - const action: string | null = queryParams.get('action'); + const queryParams = new URLSearchParams(uri.query); + const action = queryParams.get('action'); log('Opening link', uri); switch (action) { - case 'open-challenge': - that.launchMission(queryParams.get('missionId'), queryParams.get('missionVersion')); + case 'open-mission': + const missionId = queryParams.get('missionId'); + const missionVersion = queryParams.get('missionVersion'); + if (!missionId || !missionVersion) { + vscode.window.showErrorMessage('Identifiant ou version de la mission incorrect'); + } else { + that.launchMission(missionId, missionVersion); + } + break; default: @@ -64,8 +70,7 @@ export default class Controller { }, }); - const exensionStorage = ExtensionStore.getInstance(); - this.quickSetupView.isAlreadyConnected = !!(await exensionStorage.getAccessToken()); + this.quickSetupView.isAlreadyConnected = (await this.extensionStore.getAccessToken()) !== undefined; } async chooseMissionWorkdir() { const actualMissionWorkDir = this.extensionStore.getMissionWorkdir(); @@ -88,20 +93,17 @@ export default class Controller { } public async clear() { - const exensionStorage = ExtensionStore.getInstance(); - await exensionStorage.clear(); + await this.extensionStore.clear(); this.quickSetupView.isAlreadyConnected = false; } public async createSshKeyPairIfNotExist() { if (this.sshKeyManager.isSshKeyPairExist()) return; const { publicKey, privateKey } = await this.callApiService.getUserSshKey(); - this.sshKeyManager.createSshKeyFiles(publicKey, privateKey); + await this.sshKeyManager.createSshKeyFiles(publicKey, privateKey); } public async authenticate() { - // WARN generate a new device code every time student clicks on log in button. Should I keep it ?\ - // The answer might be 'yes' because when the student is already authenticated, the log in button should be disabled. await this.connection.registerDevice(); const tokens = await this.connection.getToken({ openLink: Controller.openBrowserWithUrl }); await this.extensionStore.setAccessToken(tokens.accessToken); @@ -113,46 +115,38 @@ export default class Controller { vscode.commands.executeCommand(OPEN_URL_IN_BROWSER_COMMAND.cmd, vscode.Uri.parse(url)); } - public async launchMission(missionId: string | null, missionVersion: string | null) { - console.log('LAUCH MISSION : ' + missionId); - if (missionId && missionVersion) { - vscode.window.showInformationMessage(`vous lancez la mission ${missionId}`); - const hadMissionWorkdir = this.extensionStore.getMissionWorkdir() !== undefined; - if (!hadMissionWorkdir) { - await vscode.commands.executeCommand(CHOOSE_MISSION_WORKDIR_COMMAND.cmd); - } - - const hadBeenConnected = (await this.extensionStore.getAccessToken()) !== undefined; + public async launchMission(missionId: string, missionVersion: string) { + vscode.window.showInformationMessage(`vous lancez la mission ${missionId}`); + const hadMissionWorkdir = this.extensionStore.getMissionWorkdir() !== undefined; + if (!hadMissionWorkdir) { + await vscode.commands.executeCommand(CHOOSE_MISSION_WORKDIR_COMMAND.cmd); + } - if (!hadBeenConnected) { - await this.authenticate(); - vscode.window.showInformationMessage('Nouvelle connexion validée'); - } else { - vscode.window.showInformationMessage('Déjà connecté: session récupérée'); - } + const hadBeenConnected = (await this.extensionStore.getAccessToken()) !== undefined; - const user: User = await this.callApiService.getUser(); - const giteaPublicProperties: GiteaPublicProperties = await this.callApiService.getGiteaPublicProperties(); - // TODO Should I fetch GET api/missions/{missionId} one day instead of passing necessary parameters in vscode xdg-open link ? - const mission = new Mission( - { - registryBaseURL: REGISTRY_MISSION_URL, - missionId: missionId, - missionVersion: missionVersion, - }, - user, - giteaPublicProperties, - ); - vscode.window.showInformationMessage( - 'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId, - ); - console.log('BEFORE SETUP'); - await mission.setup({}); - console.log('BEFORE open editor'); - - await mission.openEditorInFolder(); - - vscode.commands.executeCommand(OPEN_QUICK_SETUP_COMMAND.cmd); + if (!hadBeenConnected) { + await this.authenticate(); + vscode.window.showInformationMessage('Connexion validée'); } + + const user: User = await this.callApiService.getUser(); + const giteaPublicProperties: GiteaPublicProperties = await this.callApiService.getGiteaPublicProperties(); + const mission = new Mission( + { + registryBaseURL: REGISTRY_MISSION_URL, + missionId: missionId, + missionVersion: missionVersion, + }, + user, + giteaPublicProperties, + ); + vscode.window.showInformationMessage( + 'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId, + ); + await mission.setup(); + + await mission.openEditorInFolder(); + + vscode.commands.executeCommand(OPEN_QUICK_SETUP_COMMAND.cmd); } } diff --git a/deadlock-plugins/deadlock-extension/src/core/gitMission.ts b/deadlock-plugins/deadlock-extension/src/core/gitMission.ts index dbd69e6ec8f24bcfbd93e06d1b996d99554d9312..7c704b02fdb5db2b0c0412f639fb55fd4c83d22f 100644 --- a/deadlock-plugins/deadlock-extension/src/core/gitMission.ts +++ b/deadlock-plugins/deadlock-extension/src/core/gitMission.ts @@ -1,5 +1,5 @@ import simpleGit, { SimpleGit, SimpleGitOptions } from 'simple-git'; -import { error } from '../recorder/utils'; +import { error, log } from '../recorder/utils'; import { PROJECT_SRC_PATH } from './config'; import UserConfig from './userConfig'; @@ -32,10 +32,10 @@ export default class GitMission { `eval "$(ssh-agent -s)" && ssh-keyscan -p ${this.userConfig.getGiteaSshPort()} -H ${this.userConfig.getGiteaHost()} >> ~/.ssh/known_hosts`, ); - console.log(stdout); + log(stdout); if (err) { - console.error(stderr); + error(stderr); throw new Error(err); } } @@ -46,7 +46,7 @@ export default class GitMission { async init() { try { - console.log('Setup ssh agent'); + log('Setup ssh agent'); await this.setupSshAgent(); console.log('Init Git mission..'); diff --git a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts deleted file mode 100644 index fabccd3adc494949706d6f0016b7e5d8549645ab..0000000000000000000000000000000000000000 --- a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnectionVSCodeImpl.ts +++ /dev/null @@ -1,28 +0,0 @@ -import ExtensionStore from './extensionStore'; -import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection'; - -export default class KeycloakOAuth2DeviceFlowConnectionVSCodeImpl extends KeycloakOAuth2DeviceFlowConnection { - private extensionStore: ExtensionStore; - constructor(deviceUrl: string, tokenUrl: string, userInfoUrl: string) { - super(deviceUrl, tokenUrl, userInfoUrl); - this.extensionStore = ExtensionStore.getInstance(); - } - - public async getToken(args: { refreshToken?: string; openLink: (link: string) => void }): Promise<{ - accessToken: string; - refreshToken: string; - }> { - const existingAccessToken = await this.extensionStore.getAccessToken(); - const existingRefreshToken = await this.extensionStore.getRefreshToken(); - if (existingAccessToken!! && existingRefreshToken!! && (await super.tokenIsValid(existingAccessToken!))) { - return Promise.resolve({ accessToken: existingAccessToken, refreshToken: existingRefreshToken }); - } - const { refreshToken, accessToken } = await super.getToken({ - refreshToken: args.refreshToken, - openLink: args.openLink, - }); - await this.extensionStore.setAccessToken(accessToken); - await this.extensionStore.setRefreshToken(refreshToken); - return Promise.resolve({ refreshToken: refreshToken, accessToken: accessToken }); - } -} diff --git a/deadlock-plugins/deadlock-extension/src/core/metadataProvider.ts b/deadlock-plugins/deadlock-extension/src/core/metadataProvider.ts deleted file mode 100644 index a598545b60abeb4628fb4f5e15949b5dcd391b18..0000000000000000000000000000000000000000 --- a/deadlock-plugins/deadlock-extension/src/core/metadataProvider.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { exec, ExecException } from 'child_process'; -import * as fs from 'fs'; -import { userConfig } from '../extension'; -import { error as err, log } from '../recorder/utils'; -import { BASHRC_PATH, ENV_FILE_PATH } from './config'; - -export default class MetadataProvider { - public static loadPathsToJson(filePath: string) { - const portToPath = userConfig.getPaths(); - const serviceNameToPath = this.getServicePathFromPortPath(portToPath); - const jsonValue = Object.fromEntries(serviceNameToPath); - const fileContent = JSON.stringify(jsonValue, null, 4) + '\n'; - fs.writeFileSync(filePath, fileContent, { flag: 'w+' }); - } - - public static loadPathsToEnvVariables(): void { - const portToPath = userConfig.getPaths(); - const serviceNameToPath = this.getServicePathFromPortPath(portToPath); - let fileContent = ''; - for (const [serviceName, servicePath] of serviceNameToPath.entries()) { - fileContent += `${serviceName}=${servicePath} \n`; - } - fs.writeFileSync(ENV_FILE_PATH, fileContent, { flag: 'w+' }); - const bashrcAppendContent = ` -export $(cat ${ENV_FILE_PATH} | xargs -L 1) -`; - fs.writeFileSync(BASHRC_PATH, bashrcAppendContent, { flag: 'a+' }); - exec(`. ${BASHRC_PATH}`, execCallback); - } - - /** - * two formats to handle: (key="front", value="3001") | (key="8080": value="HXW3fnKwUULrfKS1-cdb")\ - * expected result: (key="front", value="localhost:3001") | (key="cdb", value="HXW3fnKwUULrfKS1-cdb") - * - */ - private static getServicePathFromPortPath(portToPath: Map<number, string>): Map<string, string> { - const serviceToPath = new Map<string, string>(); - for (const [port, path] of Object.entries(portToPath)) { - let key = ''; - let value = ''; - if (isNumeric(port)) { - key = path.split('-').slice(-1); - value = `${path}.${userConfig.getHost()}`; - } else { - key = port; - value = `localhost:${path}`; - } - serviceToPath.set(key, value); - } - return serviceToPath; - } -} - -function isNumeric(val: string) { - return /^\d+$/.test(val); -} - -function execCallback(error: ExecException | null, stdout: string, stderr: string) { - if (error) { - err(`error: ${error.message}`); - return; - } - if (stderr) { - err(`stderr: ${stderr}`); - return; - } - log(stdout); -} diff --git a/deadlock-plugins/deadlock-extension/src/extension.ts b/deadlock-plugins/deadlock-extension/src/extension.ts index e3f711319ec858062167cfa41d305f03a339eae5..b025f980911c8ad90d924c388f4821609a9a8469 100644 --- a/deadlock-plugins/deadlock-extension/src/extension.ts +++ b/deadlock-plugins/deadlock-extension/src/extension.ts @@ -9,7 +9,7 @@ export const userConfig = new UserConfigTheia(); export async function activate(context: vscode.ExtensionContext) { vscode.window.showInformationMessage('Bienvenue sur Deadlock!'); - const controller = new Controller(context); + new Controller(context); const workspaceFolders = vscode.workspace.workspaceFolders?.toString() ?? ''; if (!workspaceFolders) vscode.window.showInformationMessage('Pas de répertoires ouverts');