mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-16 17:25:58 +00:00
Add support for psql shell (#1324)
Add support for psql shell - add new terminal type - handle missing documentEndpoint property
This commit is contained in:
parent
2e618cb3c4
commit
3abbb63adc
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -35,6 +35,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": Object {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
@ -111,6 +112,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": Object {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -85,11 +85,14 @@ export function createStaticCommandBarButtons(
|
|||||||
(userContext.apiType === "Mongo" &&
|
(userContext.apiType === "Mongo" &&
|
||||||
useNotebook.getState().isShellEnabled &&
|
useNotebook.getState().isShellEnabled &&
|
||||||
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
||||||
userContext.apiType === "Cassandra"
|
userContext.apiType === "Cassandra" ||
|
||||||
|
userContext.apiType === "Postgres"
|
||||||
) {
|
) {
|
||||||
notebookButtons.push(createDivider());
|
notebookButtons.push(createDivider());
|
||||||
if (userContext.apiType === "Cassandra") {
|
if (userContext.apiType === "Cassandra") {
|
||||||
notebookButtons.push(createOpenCassandraTerminalButton(container));
|
notebookButtons.push(createOpenCassandraTerminalButton(container));
|
||||||
|
} else if (userContext.apiType === "Postgres") {
|
||||||
|
notebookButtons.push(createOpenPsqlTerminalButton(container));
|
||||||
} else {
|
} else {
|
||||||
notebookButtons.push(createOpenMongoTerminalButton(container));
|
notebookButtons.push(createOpenMongoTerminalButton(container));
|
||||||
}
|
}
|
||||||
@ -523,6 +526,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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
@ -24,6 +24,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
|||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": Object {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
|
@ -14,6 +14,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
"onRefreshResourcesClick": [Function],
|
"onRefreshResourcesClick": [Function],
|
||||||
"phoenixClient": PhoenixClient {
|
"phoenixClient": PhoenixClient {
|
||||||
|
"armResourceId": undefined,
|
||||||
"retryOptions": Object {
|
"retryOptions": Object {
|
||||||
"maxTimeout": 5000,
|
"maxTimeout": 5000,
|
||||||
"minTimeout": 5000,
|
"minTimeout": 5000,
|
||||||
|
@ -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`);
|
||||||
}
|
}
|
||||||
|
@ -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,21 @@ export class PhoenixClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getPhoenixEndpoint(): string {
|
private getPhoenixControlPlanePathPrefix(): string {
|
||||||
const phoenixEndpoint =
|
if (!this.armResourceId) {
|
||||||
|
throw new Error("The Phoenix client was not initialized properly: missing ARM resourcce id");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
@ -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("citus=>");
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user