diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 8c295bbcb..6a3a49bfb 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -394,7 +394,7 @@ export default class Explorer { useNotebook.getState().setConnectionInfo(connectionStatus); try { useNotebook.getState().setIsAllocating(true); - const connectionInfo = await this.phoenixClient.containerConnectionInfo(provisionData); + const connectionInfo = await this.phoenixClient.allocateContainer(provisionData); await this.setNotebookInfo(connectionInfo, connectionStatus); } catch (error) { connectionStatus.status = ConnectionStatusType.Failed; diff --git a/src/Explorer/Notebook/NotebookContainerClient.ts b/src/Explorer/Notebook/NotebookContainerClient.ts index fdb49195c..c0c02023c 100644 --- a/src/Explorer/Notebook/NotebookContainerClient.ts +++ b/src/Explorer/Notebook/NotebookContainerClient.ts @@ -3,7 +3,7 @@ */ import { PhoenixClient } from "Phoenix/PhoenixClient"; import * as Constants from "../../Common/Constants"; -import { ConnectionStatusType, HttpHeaders, HttpStatusCodes } from "../../Common/Constants"; +import { ConnectionStatusType, HttpHeaders } from "../../Common/Constants"; import { getErrorMessage } from "../../Common/ErrorHandlingUtils"; import * as Logger from "../../Common/Logger"; import * as DataModels from "../../Contracts/DataModels"; @@ -23,8 +23,10 @@ import { useNotebook } from "./useNotebook"; export class NotebookContainerClient { private clearReconnectionAttemptMessage? = () => {}; private isResettingWorkspace: boolean; + private phoenixClient: PhoenixClient; constructor(private onConnectionLost: () => void) { + this.phoenixClient = new PhoenixClient(); const notebookServerInfo = useNotebook.getState().notebookServerInfo; if (notebookServerInfo?.notebookServerEndpoint) { this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs); @@ -143,10 +145,7 @@ export class NotebookContainerClient { return Promise.reject(error); } - const { notebookServerEndpoint, authToken } = this.getNotebookServerConfig(); try { - let data: IPhoenixConnectionInfoResult; - let response: Response; if (NotebookUtil.isPhoenixEnabled()) { const provisionData: IProvisionData = { aadToken: userContext.authorizationToken, @@ -155,24 +154,9 @@ export class NotebookContainerClient { dbAccountName: userContext.databaseAccount.name, cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint, }; - response = await fetch(`${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer/reset`, { - method: "POST", - headers: this.getHeaders(), - body: JSON.stringify(provisionData), - }); - if (response.status === HttpStatusCodes.OK) { - data = await response.json(); - } - } else { - response = await fetch(`${notebookServerEndpoint}/api/shutdown`, { - method: "POST", - headers: { Authorization: authToken }, - }); + return await this.phoenixClient.resetContainer(provisionData); } - return { - status: response.status, - data, - }; + return null; } catch (error) { Logger.logError(getErrorMessage(error), "NotebookContainerClient/resetWorkspace"); if (!NotebookUtil.isPhoenixEnabled()) { diff --git a/src/Phoenix/PhoenixClient.ts b/src/Phoenix/PhoenixClient.ts index 344a12b4e..a52e021bc 100644 --- a/src/Phoenix/PhoenixClient.ts +++ b/src/Phoenix/PhoenixClient.ts @@ -14,15 +14,27 @@ import { userContext } from "../UserContext"; import { getAuthorizationHeader } from "../Utils/AuthorizationUtils"; export class PhoenixClient { - public async containerConnectionInfo( - provisionData: IProvisionData + private containerHealthHandler: NodeJS.Timeout; + + public async allocateContainer(provisionData: IProvisionData): Promise> { + return this.executeContainerAssignmentOperation(provisionData, "allocate"); + } + + public async resetContainer(provisionData: IProvisionData): Promise> { + return this.executeContainerAssignmentOperation(provisionData, "reset"); + } + + private async executeContainerAssignmentOperation( + provisionData: IProvisionData, + operation: string ): Promise> { try { - const response = await window.fetch(`${this.getPhoenixContainerPoolingEndPoint()}/allocate`, { + const response = await fetch(`${this.getPhoenixContainerPoolingEndPoint()}/${operation}`, { method: "POST", headers: PhoenixClient.getHeaders(), body: JSON.stringify(provisionData), }); + let data: IPhoenixConnectionInfoResult; if (response.status === HttpStatusCodes.OK) { data = await response.json(); @@ -38,12 +50,15 @@ export class PhoenixClient { } public async initiateContainerHeartBeat(containerData: { forwardingId: string; dbAccountName: string }) { - this.getContainerHealth(Notebook.containerStatusHeartbeatDelayMs, containerData); + if (this.containerHealthHandler) { + clearTimeout(this.containerHealthHandler); + } + await this.getContainerHealth(Notebook.containerStatusHeartbeatDelayMs, containerData); } private scheduleContainerHeartbeat(delayMs: number, containerData: IContainerData): void { - setTimeout(() => { - this.getContainerHealth(delayMs, containerData); + this.containerHealthHandler = setTimeout(async () => { + await this.getContainerHealth(delayMs, containerData); }, delayMs); } @@ -79,14 +94,12 @@ export class PhoenixClient { } } - private getContainerHealth(delayMs: number, containerData: { forwardingId: string; dbAccountName: string }) { - this.getContainerStatusAsync(containerData) - .then((ContainerInfo) => useNotebook.getState().setContainerStatus(ContainerInfo)) - .finally(() => { - if (useNotebook.getState().containerStatus?.status === ContainerStatusType.Active) { - this.scheduleContainerHeartbeat(delayMs, containerData); - } - }); + private async getContainerHealth(delayMs: number, containerData: { forwardingId: string; dbAccountName: string }) { + const containerInfo = await this.getContainerStatusAsync(containerData); + useNotebook.getState().setContainerStatus(containerInfo); + if (useNotebook.getState().containerStatus?.status === ContainerStatusType.Active) { + this.scheduleContainerHeartbeat(delayMs, containerData); + } } public static getPhoenixEndpoint(): string { @@ -104,6 +117,7 @@ export class PhoenixClient { public getPhoenixContainerPoolingEndPoint(): string { return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer`; } + private static getHeaders(): HeadersInit { const authorizationHeader = getAuthorizationHeader(); return {