mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-04-01 15:38:45 +01:00
Add PSQL button
This commit is contained in:
parent
c2673ec707
commit
0c25eee29c
@ -1,5 +1,5 @@
|
||||
{
|
||||
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
|
||||
"isTerminalEnabled" : false,
|
||||
"isPhoenixEnabled" : false
|
||||
}
|
||||
"JUNO_ENDPOINT": "https://localhost",
|
||||
"isTerminalEnabled": false,
|
||||
"isPhoenixEnabled": false
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ let configContext: Readonly<ConfigContext> = {
|
||||
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,
|
||||
|
@ -372,6 +372,7 @@ export enum TerminalKind {
|
||||
Default = 0,
|
||||
Mongo = 1,
|
||||
Cassandra = 2,
|
||||
PostgreSQL = 3,
|
||||
}
|
||||
|
||||
export interface DataExplorerInputsFrame {
|
||||
|
@ -74,6 +74,9 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
this.props.databaseAccount?.properties.mongoEndpoint || this.props.databaseAccount?.properties.documentEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "cassandra")) {
|
||||
terminalEndpoint = this.props.databaseAccount?.properties.cassandraEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "postgresql ")) {
|
||||
terminalEndpoint =
|
||||
this.props.databaseAccount?.properties.mongoEndpoint || this.props.databaseAccount?.properties.documentEndpoint;
|
||||
}
|
||||
|
||||
if (terminalEndpoint) {
|
||||
|
@ -20,7 +20,7 @@ import {
|
||||
ContainerConnectionInfo,
|
||||
IPhoenixConnectionInfoResult,
|
||||
IProvisionData,
|
||||
IResponse,
|
||||
IResponse
|
||||
} from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
|
||||
@ -98,7 +98,8 @@ export default class Explorer {
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
});
|
||||
this._isInitializingNotebooks = false;
|
||||
this.phoenixClient = new PhoenixClient();
|
||||
|
||||
this.phoenixClient = new PhoenixClient(userContext.databaseAccount.id);
|
||||
useNotebook.subscribe(
|
||||
() => 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");
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<Props> = ({ 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<Props> = ({ container }: Props): JSX.Ele
|
||||
)}
|
||||
</Stack>
|
||||
{!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 ? (
|
||||
<FocusTrapCallout
|
||||
role="alertdialog"
|
||||
className={styles.callout}
|
||||
|
@ -23,7 +23,7 @@ export class NotebookContainerClient {
|
||||
private scheduleTimerId: NodeJS.Timeout;
|
||||
|
||||
constructor(private onConnectionLost: () => void) {
|
||||
this.phoenixClient = new PhoenixClient();
|
||||
this.phoenixClient = new PhoenixClient(userContext.databaseAccount.id);
|
||||
this.retryOptions = {
|
||||
retries: Notebook.retryAttempts,
|
||||
maxTimeout: Notebook.retryAttemptDelayMs,
|
||||
|
@ -307,7 +307,7 @@ export const useNotebook: UseStore<NotebookState> = 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) {
|
||||
|
@ -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`);
|
||||
}
|
||||
|
32
src/Main.tsx
32
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<boolean>(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 <LoadingExplorer />;
|
||||
}
|
||||
@ -82,24 +70,6 @@ const App: React.FunctionComponent = () => {
|
||||
<CommandBar container={explorer} />
|
||||
{/* Collections Tree and Tabs - Begin */}
|
||||
<div className="resourceTreeAndTabs">
|
||||
{/* Collections Tree - Start */}
|
||||
<div id="resourcetree" data-test="resourceTreeId" className="resourceTree">
|
||||
<div className="collectionsTreeWithSplitter">
|
||||
{/* Collections Tree Expanded - Start */}
|
||||
<ResourceTreeContainer
|
||||
container={explorer}
|
||||
toggleLeftPaneExpanded={toggleLeftPaneExpanded}
|
||||
isLeftPaneExpanded={isLeftPaneExpanded}
|
||||
/>
|
||||
{/* Collections Tree Expanded - End */}
|
||||
{/* Collections Tree Collapsed - Start */}
|
||||
<CollapsedResourceTree
|
||||
toggleLeftPaneExpanded={toggleLeftPaneExpanded}
|
||||
isLeftPaneExpanded={isLeftPaneExpanded}
|
||||
/>
|
||||
{/* Collections Tree Collapsed - End */}
|
||||
</div>
|
||||
</div>
|
||||
<Tabs explorer={explorer} />
|
||||
</div>
|
||||
{/* Collections Tree and Tabs - End */}
|
||||
|
@ -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<IResponse<IPhoenixConnectionInfoResult>> {
|
||||
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 {
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user