diff --git a/configs/prod.json b/configs/prod.json index 656d09561..b4a5b6ba0 100644 --- a/configs/prod.json +++ b/configs/prod.json @@ -1,5 +1,5 @@ { - "JUNO_ENDPOINT": "https://tools.cosmos.azure.com", - "isTerminalEnabled" : false, - "isPhoenixEnabled" : false -} + "JUNO_ENDPOINT": "https://localhost", + "isTerminalEnabled": false, + "isPhoenixEnabled": false +} \ No newline at end of file diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 2e877f872..50debf196 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -423,6 +423,6 @@ export class JunoEndpoints { public static readonly Test = "https://juno-test.documents-dev.windows-int.net"; public static readonly Test2 = "https://juno-test2.documents-dev.windows-int.net"; public static readonly Test3 = "https://juno-test3.documents-dev.windows-int.net"; - public static readonly Prod = "https://tools.cosmos.azure.com"; + public static readonly Prod = "https://localhost"; public static readonly Stage = "https://tools-staging.cosmos.azure.com"; } diff --git a/src/ConfigContext.ts b/src/ConfigContext.ts index e492a3d13..210784fcd 100644 --- a/src/ConfigContext.ts +++ b/src/ConfigContext.ts @@ -69,7 +69,7 @@ let configContext: Readonly = { ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306 GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772 - JUNO_ENDPOINT: "https://tools.cosmos.azure.com", + JUNO_ENDPOINT: "https://localhost", BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com", isTerminalEnabled: false, isPhoenixEnabled: false, diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index 9e89807e2..a041e362e 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -372,6 +372,7 @@ export enum TerminalKind { Default = 0, Mongo = 1, Cassandra = 2, + PostgreSQL = 3, } export interface DataExplorerInputsFrame { diff --git a/src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx b/src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx index 9df968226..97a2994bf 100644 --- a/src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx +++ b/src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx @@ -74,6 +74,9 @@ export class NotebookTerminalComponent extends React.Component this.refreshCommandBarButtons(), (state) => state.isNotebooksEnabledForAccount @@ -356,7 +357,7 @@ export default class Explorer { (notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined)) ) { const provisionData: IProvisionData = { - cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint, + cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint, poolId: PoolIdType.DefaultPoolId, }; const connectionStatus: ContainerConnectionInfo = { @@ -1061,6 +1062,10 @@ export default class Explorer { title = "Cassandra Shell"; break; + case ViewModels.TerminalKind.PostgreSQL: + title = "PSQL Shell"; + break; + default: throw new Error("Terminal kind: ${kind} not supported"); } diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx index 3a1a9c9db..037f3d5eb 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx @@ -95,6 +95,8 @@ export function createStaticCommandBarButtons( } } + notebookButtons.push(createOpenPsqlTerminalButton(container)); + notebookButtons.forEach((btn) => { if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) { if (!useNotebook.getState().isPhoenixFeatures) { @@ -104,6 +106,10 @@ export function createStaticCommandBarButtons( if (!useNotebook.getState().isPhoenixFeatures) { applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg); } + } else if (btn.commandButtonLabel.indexOf("PSQL") !== -1) { + if (!useNotebook.getState().isPhoenixFeatures) { + applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg); + } } else if (!useNotebook.getState().isPhoenixNotebooks) { applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg); } @@ -519,6 +525,28 @@ function createOpenCassandraTerminalButton(container: Explorer): CommandButtonCo }; } +function createOpenPsqlTerminalButton(container: Explorer): CommandButtonComponentProps { + const label = "Open PSQL Shell"; + const disableButton = + !useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled; + return { + iconSrc: HostedTerminalIcon, + iconAlt: label, + onCommandClick: () => { + if (useNotebook.getState().isNotebookEnabled) { + container.openNotebookTerminal(ViewModels.TerminalKind.PostgreSQL); + } + }, + commandButtonLabel: label, + hasPopup: false, + disabled: disableButton, + ariaLabel: label, + tooltipText: !disableButton + ? "" + : "This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.", + }; +} + function createNotebookWorkspaceResetButton(container: Explorer): CommandButtonComponentProps { const label = "Reset Workspace"; return { diff --git a/src/Explorer/Menus/CommandBar/ConnectionStatusComponent.tsx b/src/Explorer/Menus/CommandBar/ConnectionStatusComponent.tsx index 520608acd..3ba363da8 100644 --- a/src/Explorer/Menus/CommandBar/ConnectionStatusComponent.tsx +++ b/src/Explorer/Menus/CommandBar/ConnectionStatusComponent.tsx @@ -8,7 +8,7 @@ import { ProgressIndicator, Stack, Text, - TooltipHost, + TooltipHost } from "@fluentui/react"; import { useId } from "@fluentui/react-hooks"; import { ActionButton, DefaultButton } from "@fluentui/react/lib/Button"; @@ -124,8 +124,8 @@ export const ConnectionStatus: React.FC = ({ container }: Props): JSX.Ele content={ containerInfo?.status === ContainerStatusType.Active ? `Connected to temporary workspace. This temporary workspace will get disconnected in ${Math.round( - containerInfo.durationLeftInMinutes - )} minutes.` + containerInfo.durationLeftInMinutes + )} minutes.` : toolTipContent } > @@ -153,9 +153,9 @@ export const ConnectionStatus: React.FC = ({ container }: Props): JSX.Ele )} {!isBarDismissed && - containerInfo.status && - containerInfo.status === ContainerStatusType.Active && - Math.round(containerInfo.durationLeftInMinutes) <= Notebook.remainingTimeForAlert ? ( + containerInfo.status && + containerInfo.status === ContainerStatusType.Active && + Math.round(containerInfo.durationLeftInMinutes) <= Notebook.remainingTimeForAlert ? ( void) { - this.phoenixClient = new PhoenixClient(); + this.phoenixClient = new PhoenixClient(userContext.databaseAccount.id); this.retryOptions = { retries: Notebook.retryAttempts, maxTimeout: Notebook.retryAttemptDelayMs, diff --git a/src/Explorer/Notebook/useNotebook.ts b/src/Explorer/Notebook/useNotebook.ts index d283b7a7b..99e7d7933 100644 --- a/src/Explorer/Notebook/useNotebook.ts +++ b/src/Explorer/Notebook/useNotebook.ts @@ -307,7 +307,7 @@ export const useNotebook: UseStore = create((set, get) => ({ let isPhoenixFeatures = false; const isPublicInternetAllowed = isPublicInternetAccessAllowed(); - const phoenixClient = new PhoenixClient(); + const phoenixClient = new PhoenixClient(userContext.databaseAccount.id); const dbAccountAllowedInfo = await phoenixClient.getDbAccountAllowedStatus(); if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) { diff --git a/src/Explorer/Tabs/TerminalTab.tsx b/src/Explorer/Tabs/TerminalTab.tsx index 1c010d6b3..86d8b3f6c 100644 --- a/src/Explorer/Tabs/TerminalTab.tsx +++ b/src/Explorer/Tabs/TerminalTab.tsx @@ -95,6 +95,10 @@ export default class TerminalTab extends TabsBase { endpointSuffix = "cassandra"; break; + case ViewModels.TerminalKind.PostgreSQL: + endpointSuffix = "postgresql"; + break; + default: throw new Error(`Terminal kind: ${options.kind} not supported`); } diff --git a/src/Main.tsx b/src/Main.tsx index 20a7340ee..8d4866933 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -4,7 +4,7 @@ import "bootstrap/dist/css/bootstrap.css"; import { QuickstartCarousel } from "Explorer/Tutorials/QuickstartCarousel"; import { QuickstartTutorial } from "Explorer/Tutorials/QuickstartTutorial"; import { useCarousel } from "hooks/useCarousel"; -import React, { useState } from "react"; +import React from "react"; import ReactDOM from "react-dom"; import "../externals/jquery-ui.min.css"; import "../externals/jquery-ui.min.js"; @@ -28,8 +28,6 @@ import "../less/TableStyles/EntityEditor.less"; import "../less/TableStyles/fulldatatables.less"; import "../less/TableStyles/queryBuilder.less"; import "../less/tree.less"; -import { CollapsedResourceTree } from "./Common/CollapsedResourceTree"; -import { ResourceTreeContainer } from "./Common/ResourceTreeContainer"; import "./Explorer/Controls/Accordion/AccordionComponent.less"; import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less"; import { Dialog } from "./Explorer/Controls/Dialog"; @@ -56,21 +54,11 @@ import "./Shared/appInsights"; initializeIcons(); const App: React.FunctionComponent = () => { - const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState(true); const isCarouselOpen = useCarousel((state) => state.shouldOpen); const config = useConfig(); const explorer = useKnockoutExplorer(config?.platform); - const toggleLeftPaneExpanded = () => { - setIsLeftPaneExpanded(!isLeftPaneExpanded); - if (isLeftPaneExpanded) { - document.getElementById("expandToggleLeftPaneButton").focus(); - } else { - document.getElementById("collapseToggleLeftPaneButton").focus(); - } - }; - if (!explorer) { return ; } @@ -82,24 +70,6 @@ const App: React.FunctionComponent = () => { {/* Collections Tree and Tabs - Begin */}
- {/* Collections Tree - Start */} -
-
- {/* Collections Tree Expanded - Start */} - - {/* Collections Tree Expanded - End */} - {/* Collections Tree Collapsed - Start */} - - {/* Collections Tree Collapsed - End */} -
-
{/* Collections Tree and Tabs - End */} diff --git a/src/Phoenix/PhoenixClient.ts b/src/Phoenix/PhoenixClient.ts index 664e595eb..cd5aebbad 100644 --- a/src/Phoenix/PhoenixClient.ts +++ b/src/Phoenix/PhoenixClient.ts @@ -8,7 +8,7 @@ import { ContainerStatusType, HttpHeaders, HttpStatusCodes, - Notebook, + Notebook } from "../Common/Constants"; import { getErrorMessage, getErrorStack } from "../Common/ErrorHandlingUtils"; import * as Logger from "../Common/Logger"; @@ -23,7 +23,7 @@ import { IPhoenixError, IProvisionData, IResponse, - PhoenixErrorType, + PhoenixErrorType } from "../Contracts/DataModels"; import { useNotebook } from "../Explorer/Notebook/useNotebook"; import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor"; @@ -31,6 +31,7 @@ import { userContext } from "../UserContext"; import { getAuthorizationHeader } from "../Utils/AuthorizationUtils"; export class PhoenixClient { + private armResourceId: string; private containerHealthHandler: NodeJS.Timeout; private retryOptions: promiseRetry.Options = { retries: Notebook.retryAttempts, @@ -38,6 +39,10 @@ export class PhoenixClient { minTimeout: Notebook.retryAttemptDelayMs, }; + constructor(armResourceId: string) { + this.armResourceId = armResourceId; + } + public async allocateContainer(provisionData: IProvisionData): Promise> { return this.executeContainerAssignmentOperation(provisionData, "allocate"); } @@ -205,22 +210,17 @@ export class PhoenixClient { } } - public static getPhoenixEndpoint(): string { - const phoenixEndpoint = + public getPhoenixControlPlanePathPrefix(): string { + const toolsEndpoint = userContext.features.phoenixEndpoint ?? userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT; - if (!validateEndpoint(phoenixEndpoint, allowedJunoOrigins)) { - const error = `${phoenixEndpoint} not allowed as juno endpoint`; + + if (!validateEndpoint(toolsEndpoint, allowedJunoOrigins)) { + const error = `${toolsEndpoint} not allowed as tools endpoint`; console.error(error); throw new Error(error); } - return phoenixEndpoint; - } - - public getPhoenixControlPlanePathPrefix(): string { - return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer/cosmosaccounts${ - userContext.databaseAccount.id - }`; + return `${toolsEndpoint}/api/controlplane/toolscontainer/cosmosaccounts${this.armResourceId}`; } private static getHeaders(): HeadersInit { diff --git a/src/Terminal/JupyterLabAppFactory.ts b/src/Terminal/JupyterLabAppFactory.ts index d4edb9c0e..fa2ce4772 100644 --- a/src/Terminal/JupyterLabAppFactory.ts +++ b/src/Terminal/JupyterLabAppFactory.ts @@ -17,7 +17,7 @@ export class JupyterLabAppFactory { } private isMongoShellStarted(content: string | undefined) { - this.isShellStarted = content?.includes("MongoDB shell version"); + this.isShellStarted = content?.includes("MongoDB shell version") || content?.includes("citus=>"); } private isCassandraShellStarted(content: string | undefined) {