Compare commits

...

5 Commits

Author SHA1 Message Date
artrejo
fe3e973976 Rename psql TerminalKind enum 2022-09-19 14:06:29 -07:00
artrejo
66ca9c9557 Handle PostgresShell Started 2022-09-19 13:51:59 -07:00
artrejo
0ba704b592 Fix message por psql terminal 2022-09-19 13:03:58 -07:00
artrejo
1285d86865 Merge branch 'master' of https://github.com/Azure/cosmos-explorer into users/artrejo/pgshell 2022-09-16 01:00:21 -07:00
artrejo
0c25eee29c Add PSQL button 2022-09-16 00:17:07 -07:00
13 changed files with 68 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
{ {
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com", "JUNO_ENDPOINT": "https://localhost",
"isTerminalEnabled": false, "isTerminalEnabled": false,
"isPhoenixEnabled": false "isPhoenixEnabled": false
} }

View File

@@ -423,6 +423,6 @@ export class JunoEndpoints {
public static readonly Test = "https://juno-test.documents-dev.windows-int.net"; 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 Test2 = "https://juno-test2.documents-dev.windows-int.net";
public static readonly Test3 = "https://juno-test3.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"; public static readonly Stage = "https://tools-staging.cosmos.azure.com";
} }

View File

@@ -69,7 +69,7 @@ let configContext: Readonly<ConfigContext> = {
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306 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 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", BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
isTerminalEnabled: false, isTerminalEnabled: false,
isPhoenixEnabled: false, isPhoenixEnabled: false,

View File

@@ -372,6 +372,7 @@ export enum TerminalKind {
Default = 0, Default = 0,
Mongo = 1, Mongo = 1,
Cassandra = 2, Cassandra = 2,
Postgres = 3,
} }
export interface DataExplorerInputsFrame { export interface DataExplorerInputsFrame {

View File

@@ -74,6 +74,8 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
this.props.databaseAccount?.properties.mongoEndpoint || this.props.databaseAccount?.properties.documentEndpoint; this.props.databaseAccount?.properties.mongoEndpoint || this.props.databaseAccount?.properties.documentEndpoint;
} else if (StringUtils.endsWith(notebookServerEndpoint, "cassandra")) { } else if (StringUtils.endsWith(notebookServerEndpoint, "cassandra")) {
terminalEndpoint = this.props.databaseAccount?.properties.cassandraEndpoint; terminalEndpoint = this.props.databaseAccount?.properties.cassandraEndpoint;
} else if (StringUtils.endsWith(notebookServerEndpoint, "postgresql")) {
return (this.props.databaseAccount?.properties as any).postgresqlEndpoint;
} }
if (terminalEndpoint) { if (terminalEndpoint) {

View File

@@ -93,7 +93,8 @@ export default class Explorer {
dataExplorerArea: Constants.Areas.ResourceTree, dataExplorerArea: Constants.Areas.ResourceTree,
}); });
this._isInitializingNotebooks = false; this._isInitializingNotebooks = false;
this.phoenixClient = new PhoenixClient();
this.phoenixClient = new PhoenixClient(userContext.databaseAccount.id);
useNotebook.subscribe( useNotebook.subscribe(
() => this.refreshCommandBarButtons(), () => this.refreshCommandBarButtons(),
(state) => state.isNotebooksEnabledForAccount (state) => state.isNotebooksEnabledForAccount
@@ -353,7 +354,7 @@ export default class Explorer {
(notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined)) (notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined))
) { ) {
const provisionData: IProvisionData = { const provisionData: IProvisionData = {
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint, cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
poolId: PoolIdType.DefaultPoolId, poolId: PoolIdType.DefaultPoolId,
}; };
const connectionStatus: ContainerConnectionInfo = { const connectionStatus: ContainerConnectionInfo = {
@@ -1058,6 +1059,10 @@ export default class Explorer {
title = "Cassandra Shell"; title = "Cassandra Shell";
break; break;
case ViewModels.TerminalKind.Postgres:
title = "PSQL Shell";
break;
default: default:
throw new Error("Terminal kind: ${kind} not supported"); throw new Error("Terminal kind: ${kind} not supported");
} }

View File

@@ -95,6 +95,8 @@ export function createStaticCommandBarButtons(
} }
} }
notebookButtons.push(createOpenPsqlTerminalButton(container));
notebookButtons.forEach((btn) => { notebookButtons.forEach((btn) => {
if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) { if (btn.commandButtonLabel.indexOf("Cassandra") !== -1) {
if (!useNotebook.getState().isPhoenixFeatures) { if (!useNotebook.getState().isPhoenixFeatures) {
@@ -104,6 +106,10 @@ export function createStaticCommandBarButtons(
if (!useNotebook.getState().isPhoenixFeatures) { if (!useNotebook.getState().isPhoenixFeatures) {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg); applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg);
} }
} else if (btn.commandButtonLabel.indexOf("PSQL") !== -1) {
if (!useNotebook.getState().isPhoenixFeatures) {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg);
}
} else if (btn.commandButtonLabel.indexOf("Open Terminal") !== -1) { } else if (btn.commandButtonLabel.indexOf("Open Terminal") !== -1) {
if (!useNotebook.getState().isPhoenixFeatures) { if (!useNotebook.getState().isPhoenixFeatures) {
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg); applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
@@ -523,6 +529,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.Postgres);
}
},
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 { function createNotebookWorkspaceResetButton(container: Explorer): CommandButtonComponentProps {
const label = "Reset Workspace"; const label = "Reset Workspace";
return { return {

View File

@@ -23,7 +23,7 @@ export class NotebookContainerClient {
private scheduleTimerId: NodeJS.Timeout; private scheduleTimerId: NodeJS.Timeout;
constructor(private onConnectionLost: () => void) { constructor(private onConnectionLost: () => void) {
this.phoenixClient = new PhoenixClient(); this.phoenixClient = new PhoenixClient(userContext.databaseAccount.id);
this.retryOptions = { this.retryOptions = {
retries: Notebook.retryAttempts, retries: Notebook.retryAttempts,
maxTimeout: Notebook.retryAttemptDelayMs, maxTimeout: Notebook.retryAttemptDelayMs,

View File

@@ -307,7 +307,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
let isPhoenixFeatures = false; let isPhoenixFeatures = false;
const isPublicInternetAllowed = isPublicInternetAccessAllowed(); const isPublicInternetAllowed = isPublicInternetAccessAllowed();
const phoenixClient = new PhoenixClient(); const phoenixClient = new PhoenixClient(userContext.databaseAccount.id);
const dbAccountAllowedInfo = await phoenixClient.getDbAccountAllowedStatus(); const dbAccountAllowedInfo = await phoenixClient.getDbAccountAllowedStatus();
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) { if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {

View File

@@ -95,6 +95,10 @@ export default class TerminalTab extends TabsBase {
endpointSuffix = "cassandra"; endpointSuffix = "cassandra";
break; break;
case ViewModels.TerminalKind.Postgres:
endpointSuffix = "postgresql";
break;
default: default:
throw new Error(`Terminal kind: ${options.kind} not supported`); throw new Error(`Terminal kind: ${options.kind} not supported`);
} }

View File

@@ -7,7 +7,6 @@ import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuicksta
import { useCarousel } from "hooks/useCarousel"; import { useCarousel } from "hooks/useCarousel";
import React, { useState } from "react"; import React, { useState } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { userContext } from "UserContext";
import "../externals/jquery-ui.min.css"; import "../externals/jquery-ui.min.css";
import "../externals/jquery-ui.min.js"; import "../externals/jquery-ui.min.js";
import "../externals/jquery-ui.structure.min.css"; import "../externals/jquery-ui.structure.min.css";
@@ -30,8 +29,6 @@ import "../less/TableStyles/EntityEditor.less";
import "../less/TableStyles/fulldatatables.less"; import "../less/TableStyles/fulldatatables.less";
import "../less/TableStyles/queryBuilder.less"; import "../less/TableStyles/queryBuilder.less";
import "../less/tree.less"; import "../less/tree.less";
import { CollapsedResourceTree } from "./Common/CollapsedResourceTree";
import { ResourceTreeContainer } from "./Common/ResourceTreeContainer";
import "./Explorer/Controls/Accordion/AccordionComponent.less"; import "./Explorer/Controls/Accordion/AccordionComponent.less";
import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less"; import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
import { Dialog } from "./Explorer/Controls/Dialog"; import { Dialog } from "./Explorer/Controls/Dialog";
@@ -84,26 +81,6 @@ const App: React.FunctionComponent = () => {
<CommandBar container={explorer} /> <CommandBar container={explorer} />
{/* Collections Tree and Tabs - Begin */} {/* Collections Tree and Tabs - Begin */}
<div className="resourceTreeAndTabs"> <div className="resourceTreeAndTabs">
{/* Collections Tree - Start */}
{userContext.apiType !== "Postgres" && (
<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} /> <Tabs explorer={explorer} />
</div> </div>
{/* Collections Tree and Tabs - End */} {/* Collections Tree and Tabs - End */}

View File

@@ -32,6 +32,7 @@ import { userContext } from "../UserContext";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils"; import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
export class PhoenixClient { export class PhoenixClient {
private armResourceId: string;
private containerHealthHandler: NodeJS.Timeout; private containerHealthHandler: NodeJS.Timeout;
private retryOptions: promiseRetry.Options = { private retryOptions: promiseRetry.Options = {
retries: Notebook.retryAttempts, retries: Notebook.retryAttempts,
@@ -39,6 +40,10 @@ export class PhoenixClient {
minTimeout: Notebook.retryAttemptDelayMs, minTimeout: Notebook.retryAttemptDelayMs,
}; };
constructor(armResourceId: string) {
this.armResourceId = armResourceId;
}
public async allocateContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixServiceInfo>> { public async allocateContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixServiceInfo>> {
return this.executeContainerAssignmentOperation(provisionData, "allocate"); return this.executeContainerAssignmentOperation(provisionData, "allocate");
} }
@@ -214,22 +219,17 @@ export class PhoenixClient {
} }
} }
public static getPhoenixEndpoint(): string { public getPhoenixControlPlanePathPrefix(): string {
const phoenixEndpoint = const toolsEndpoint =
userContext.features.phoenixEndpoint ?? userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT; 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); console.error(error);
throw new Error(error); throw new Error(error);
} }
return phoenixEndpoint; return `${toolsEndpoint}/api/controlplane/toolscontainer/cosmosaccounts${this.armResourceId}`;
}
public getPhoenixControlPlanePathPrefix(): string {
return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer/cosmosaccounts${
userContext.databaseAccount.id
}`;
} }
private static getHeaders(): HeadersInit { private static getHeaders(): HeadersInit {

View File

@@ -24,6 +24,10 @@ export class JupyterLabAppFactory {
this.isShellStarted = content?.includes("Connected to") && content?.includes("cqlsh"); this.isShellStarted = content?.includes("Connected to") && content?.includes("cqlsh");
} }
private isPostgresShellStarted(content: string | undefined) {
this.isShellStarted = content?.includes("cqlsh");
}
constructor(closeTab: () => void) { constructor(closeTab: () => void) {
this.onShellExited = closeTab; this.onShellExited = closeTab;
this.isShellStarted = false; this.isShellStarted = false;
@@ -36,6 +40,9 @@ export class JupyterLabAppFactory {
case "Cassandra": case "Cassandra":
this.checkShellStarted = this.isCassandraShellStarted; this.checkShellStarted = this.isCassandraShellStarted;
break; break;
case "Postgres":
this.checkShellStarted = this.isPostgresShellStarted;
break;
} }
} }