import * as vscode from 'vscode'; import { KEYCLOAK_DEVICE_AUTH_URL, KEYCLOAK_TOKEN_CREATE_URL, KEYCLOAK_USER_INFO_URL } from '../config'; import { OPEN_GETTING_STARTED_COMMAND } from '../theia/command'; import BriefingView from '../view/briefingView'; import GettingStartedView from '../view/gettingStartedView'; import { CHOOSE_MISSION_WORKDIR_COMMAND, CommandHandler } from './commandHandler'; import ExtensionStore from './extensionStore'; import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection'; export default class Controller { public connection: KeycloakOAuth2DeviceFlowConnection; private commandHandler: CommandHandler; private briefingView: BriefingView; private gettingStartedView: GettingStartedView; private extensionStore: ExtensionStore; constructor(private context: vscode.ExtensionContext) { this.briefingView = new BriefingView(); this.gettingStartedView = new GettingStartedView(context.extensionUri); this.commandHandler = new CommandHandler(this); this.connection = new KeycloakOAuth2DeviceFlowConnection( KEYCLOAK_DEVICE_AUTH_URL, KEYCLOAK_TOKEN_CREATE_URL, KEYCLOAK_USER_INFO_URL, ); this.extensionStore = ExtensionStore.getInstance(); this.init(); } private async init() { const that = this; vscode.window.registerUriHandler({ handleUri(uri: vscode.Uri) { const queryParams: URLSearchParams = new URLSearchParams(uri.query); const action: string | null = queryParams.get('action'); switch (action) { case 'open-challenge': that.launchMission(queryParams.get('missionId')); break; default: vscode.window.showInformationMessage('Aucune action trouvée!'); } }, }); const exensionStorage = ExtensionStore.getInstance(); this.gettingStartedView.isAlreadyConnected = !!(await exensionStorage.getAccessToken()); } async chooseMissionWorkdir() { const actualMissionWorkDir = this.extensionStore.getMissionWorkdir(); const folderUri = await vscode.window.showOpenDialog({ defaultUri: actualMissionWorkDir ? vscode.Uri.file(actualMissionWorkDir) : undefined, canSelectFolders: true, canSelectFiles: false, title: 'Choisis le dossier qui contiendra tes missions', }); if (!folderUri) { if (this.extensionStore.getMissionWorkdir()) { return; } this.chooseMissionWorkdir(); } else { this.extensionStore.setMissionWorkdir(folderUri[0].path); } } public async clear() { const exensionStorage = ExtensionStore.getInstance(); await exensionStorage.clear(); this.gettingStartedView.isAlreadyConnected = false; } 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); await this.extensionStore.setRefreshToken(tokens.refreshToken); this.gettingStartedView.isAlreadyConnected = true; } public static openBrowserWithUrl(url: string) { vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(url)); } public async launchMission(missionId: string | null) { if (missionId) { 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; if (!hadBeenConnected) { this.authenticate(); vscode.window.showInformationMessage('Nouvelle connexion validée'); } else { vscode.window.showInformationMessage('Déjà connecté: session récupérée'); } vscode.commands.executeCommand(OPEN_GETTING_STARTED_COMMAND.cmd); } } }