mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 23:16:56 +00:00
Integrate PSQL shell in quick start guide (#1333)
This commit is contained in:
parent
8433a027ad
commit
e909ac43f4
@ -33,6 +33,7 @@ export interface DatabaseAccountExtendedProperties {
|
||||
privateEndpointConnections?: unknown[];
|
||||
capacity?: { totalThroughputLimit: number };
|
||||
locations?: DatabaseAccountResponseLocation[];
|
||||
postgresqlEndpoint?: string;
|
||||
}
|
||||
|
||||
export interface DatabaseAccountResponseLocation {
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Wrapper around Notebook server terminal
|
||||
*/
|
||||
|
||||
import { useTerminal } from "hooks/useTerminal";
|
||||
import postRobot from "post-robot";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
@ -40,6 +41,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
|
||||
handleFrameLoad(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {
|
||||
this.terminalWindow = (event.target as HTMLIFrameElement).contentWindow;
|
||||
useTerminal.getState().setTerminal(this.terminalWindow);
|
||||
this.sendPropsToTerminalFrame();
|
||||
}
|
||||
|
||||
@ -75,7 +77,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "cassandra")) {
|
||||
terminalEndpoint = this.props.databaseAccount?.properties.cassandraEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "postgresql")) {
|
||||
return (this.props.databaseAccount?.properties as any).postgresqlEndpoint;
|
||||
return this.props.databaseAccount?.properties.postgresqlEndpoint;
|
||||
}
|
||||
|
||||
if (terminalEndpoint) {
|
||||
|
@ -186,9 +186,7 @@ export default class Explorer {
|
||||
useNotebook.getState().setNotebookBasePath(userContext.features.notebookBasePath);
|
||||
}
|
||||
|
||||
if (userContext.apiType !== "Postgres") {
|
||||
this.refreshExplorer();
|
||||
}
|
||||
this.refreshExplorer();
|
||||
}
|
||||
|
||||
public async initiateAndRefreshNotebookList(): Promise<void> {
|
||||
@ -1249,9 +1247,11 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public async refreshExplorer(): Promise<void> {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
: this.refreshAllDatabases();
|
||||
if (userContext.apiType !== "Postgres") {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
: this.refreshAllDatabases();
|
||||
}
|
||||
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
||||
|
||||
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
||||
|
@ -85,14 +85,11 @@ export function createStaticCommandBarButtons(
|
||||
(userContext.apiType === "Mongo" &&
|
||||
useNotebook.getState().isShellEnabled &&
|
||||
selectedNodeState.isDatabaseNodeOrNoneSelected()) ||
|
||||
userContext.apiType === "Cassandra" ||
|
||||
userContext.apiType === "Postgres"
|
||||
userContext.apiType === "Cassandra"
|
||||
) {
|
||||
notebookButtons.push(createDivider());
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
notebookButtons.push(createOpenCassandraTerminalButton(container));
|
||||
} else if (userContext.apiType === "Postgres") {
|
||||
notebookButtons.push(createOpenPsqlTerminalButton(container));
|
||||
} else {
|
||||
notebookButtons.push(createOpenMongoTerminalButton(container));
|
||||
}
|
||||
@ -612,16 +609,7 @@ function createStaticCommandBarButtonsForResourceToken(
|
||||
}
|
||||
|
||||
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const postgreShellLabel = "Open PostgreSQL Shell";
|
||||
const openPostgreShellBtn = {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: postgreShellLabel,
|
||||
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Mongo),
|
||||
commandButtonLabel: postgreShellLabel,
|
||||
hasPopup: false,
|
||||
disabled: false,
|
||||
ariaLabel: postgreShellLabel,
|
||||
};
|
||||
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
|
||||
|
||||
return [openPostgreShellBtn];
|
||||
}
|
||||
|
@ -124,8 +124,9 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
}
|
||||
|
||||
const firstWriteLocation =
|
||||
databaseAccount?.properties?.writeLocations &&
|
||||
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
|
||||
userContext.apiType === "Postgres"
|
||||
? databaseAccount?.location
|
||||
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
|
||||
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
try {
|
||||
@ -313,7 +314,10 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {
|
||||
if (dbAccountAllowedInfo?.type === PhoenixErrorType.PhoenixFlightFallback) {
|
||||
isPhoenixNotebooks = isPublicInternetAllowed && userContext.features.phoenixNotebooks === true;
|
||||
isPhoenixFeatures = isPublicInternetAllowed && userContext.features.phoenixFeatures === true;
|
||||
isPhoenixFeatures =
|
||||
isPublicInternetAllowed &&
|
||||
// phoenix needs to be enabled for Postgres accounts since the PSQL shell requires phoenix containers
|
||||
(userContext.features.phoenixFeatures === true || userContext.apiType === "Postgres");
|
||||
} else {
|
||||
isPhoenixNotebooks = isPhoenixFeatures = isPublicInternetAllowed;
|
||||
}
|
||||
|
105
src/Explorer/Quickstart/PostgreQuickstartCommands.ts
Normal file
105
src/Explorer/Quickstart/PostgreQuickstartCommands.ts
Normal file
@ -0,0 +1,105 @@
|
||||
export const newTableCommand = `DROP SCHEMA IF EXISTS cosmosdb_tutorial CASCADE;
|
||||
CREATE SCHEMA cosmosdb_tutorial;
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
CREATE TABLE github_users
|
||||
(
|
||||
user_id bigint,
|
||||
url text,
|
||||
login text,
|
||||
avatar_url text,
|
||||
gravatar_id text,
|
||||
display_login text
|
||||
);
|
||||
CREATE TABLE github_events
|
||||
(
|
||||
event_id bigint,
|
||||
event_type text,
|
||||
event_public boolean,
|
||||
repo_id bigint,
|
||||
payload jsonb,
|
||||
repo jsonb,
|
||||
user_id bigint,
|
||||
org jsonb,
|
||||
created_at timestamp
|
||||
);
|
||||
CREATE INDEX event_type_index ON github_events (event_type);
|
||||
CREATE INDEX payload_index ON github_events USING GIN (payload jsonb_path_ops);
|
||||
`;
|
||||
|
||||
export const newTableCommandForDisplay = `DROP SCHEMA IF EXISTS cosmosdb_tutorial CASCADE;
|
||||
CREATE SCHEMA cosmosdb_tutorial;
|
||||
|
||||
-- Using schema created for tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
CREATE TABLE github_users
|
||||
(
|
||||
user_id bigint,
|
||||
url text,
|
||||
login text,
|
||||
avatar_url text,
|
||||
gravatar_id text,
|
||||
display_login text
|
||||
);
|
||||
|
||||
CREATE TABLE github_events
|
||||
(
|
||||
event_id bigint,
|
||||
event_type text,
|
||||
event_public boolean,
|
||||
repo_id bigint,
|
||||
payload jsonb,
|
||||
repo jsonb,
|
||||
user_id bigint,
|
||||
org jsonb,
|
||||
created_at timestamp
|
||||
);
|
||||
|
||||
--Create indexes on events table
|
||||
CREATE INDEX event_type_index ON github_events (event_type);
|
||||
CREATE INDEX payload_index ON github_events USING GIN (payload jsonb_path_ops);`;
|
||||
|
||||
export const distributeTableCommand = `SET search_path to cosmosdb_tutorial;
|
||||
SELECT create_distributed_table('github_users', 'user_id');
|
||||
SELECT create_distributed_table('github_events', 'user_id');
|
||||
`;
|
||||
|
||||
export const distributeTableCommandForDisplay = `-- Using schema created for the tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
SELECT create_distributed_table('github_users', 'user_id');
|
||||
SELECT create_distributed_table('github_events', 'user_id');`;
|
||||
|
||||
export const loadDataCommand = `SET search_path to cosmosdb_tutorial;
|
||||
\\COPY github_users FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/users.csv"' WITH (FORMAT CSV);
|
||||
\\COPY github_events FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/events.csv"' WITH (FORMAT CSV);
|
||||
`;
|
||||
|
||||
export const loadDataCommandForDisplay = `-- Using schema created for the tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
-- download users and store in table
|
||||
\\COPY github_users FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/users.csv"' WITH (FORMAT CSV);
|
||||
\\COPY github_events FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/events.csv"' WITH (FORMAT CSV);`;
|
||||
|
||||
export const queryCommand = `SET search_path to cosmosdb_tutorial;
|
||||
SELECT count(*) FROM github_users;
|
||||
SELECT created_at, event_type, repo->>'name' AS repo_name
|
||||
FROM github_events
|
||||
WHERE user_id = 3861633;
|
||||
SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::int) AS num_commits FROM github_events WHERE event_type = 'PushEvent' AND payload @> '{"ref":"refs/heads/master"}' GROUP BY hour ORDER BY hour;
|
||||
`;
|
||||
|
||||
export const queryCommandForDisplay = `-- Using schema created for the tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
-- count all rows (across shards)
|
||||
SELECT count(*) FROM github_users;
|
||||
|
||||
-- Find all events for a single user.
|
||||
SELECT created_at, event_type, repo->>'name' AS repo_name
|
||||
FROM github_events
|
||||
WHERE user_id = 3861633;
|
||||
|
||||
-- Find the number of commits on the master branch per hour
|
||||
SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::int) AS num_commits FROM github_events WHERE event_type = 'PushEvent' AND payload @> '{"ref":"refs/heads/master"}' GROUP BY hour ORDER BY hour;`;
|
@ -11,6 +11,17 @@ import {
|
||||
Text,
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import {
|
||||
distributeTableCommand,
|
||||
distributeTableCommandForDisplay,
|
||||
loadDataCommand,
|
||||
loadDataCommandForDisplay,
|
||||
newTableCommand,
|
||||
newTableCommandForDisplay,
|
||||
queryCommand,
|
||||
queryCommandForDisplay,
|
||||
} from "Explorer/Quickstart/PostgreQuickstartCommands";
|
||||
import { useTerminal } from "hooks/useTerminal";
|
||||
import React, { useState } from "react";
|
||||
import Youtube from "react-youtube";
|
||||
import Pivot1SelectedIcon from "../../../images/Pivot1_selected.svg";
|
||||
@ -35,65 +46,6 @@ enum GuideSteps {
|
||||
|
||||
export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
const [currentStep, setCurrentStep] = useState<number>(0);
|
||||
const newTableCommand = `DROP SCHEMA IF EXISTS cosmosdb_tutorial CASCADE;
|
||||
CREATE SCHEMA cosmosdb_tutorial;
|
||||
|
||||
-- Using schema created for tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
CREATE TABLE github_users
|
||||
(
|
||||
user_id bigint,
|
||||
url text,
|
||||
login text,
|
||||
avatar_url text,
|
||||
gravatar_id text,
|
||||
display_login text
|
||||
);
|
||||
|
||||
CREATE TABLE github_events
|
||||
(
|
||||
event_id bigint,
|
||||
event_type text,
|
||||
event_public boolean,
|
||||
repo_id bigint,
|
||||
payload jsonb,
|
||||
repo jsonb,
|
||||
user_id bigint,
|
||||
org jsonb,
|
||||
created_at timestamp
|
||||
);
|
||||
|
||||
--Create indexes on events table
|
||||
CREATE INDEX event_type_index ON github_events (event_type);
|
||||
CREATE INDEX payload_index ON github_events USING GIN (payload jsonb_path_ops); `;
|
||||
|
||||
const distributeTableCommand = `-- Using schema created for the tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
SELECT create_distributed_table('github_users', 'user_id');
|
||||
SELECT create_distributed_table('github_events', 'user_id'); `;
|
||||
|
||||
const loadDataCommand = `-- Using schema created for the tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
-- download users and store in table
|
||||
\\COPY github_users FROM PROGRAM 'curl https://examples.citusdata.com/users.csv' WITH (FORMAT CSV)
|
||||
\\COPY github_events FROM PROGRAM 'curl https://examples.citusdata.com/events.csv' WITH (FORMAT CSV) `;
|
||||
|
||||
const queryCommand = `-- Using schema created for the tutorial
|
||||
SET search_path to cosmosdb_tutorial;
|
||||
|
||||
-- count all rows (across shards)
|
||||
SELECT count(*) FROM github_users;
|
||||
|
||||
-- Find all events for a single user.
|
||||
SELECT created_at, event_type, repo->>'name' AS repo_name
|
||||
FROM github_events
|
||||
WHERE user_id = 3861633;
|
||||
|
||||
-- Find the number of commits on the master branch per hour
|
||||
SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::int) AS num_commits FROM github_events WHERE event_type = 'PushEvent' AND payload @> '{"ref":"refs/heads/master"}' GROUP BY hour ORDER BY hour; `;
|
||||
|
||||
const onCopyBtnClicked = (selector: string): void => {
|
||||
const textfield: HTMLInputElement = document.querySelector(selector);
|
||||
@ -143,7 +95,6 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
<Stack style={{ paddingTop: 8, height: "100%", width: "100%" }}>
|
||||
<Stack style={{ flexGrow: 1, padding: "0 20px", overflow: "auto" }}>
|
||||
<Text variant="xxLarge">Quick start guide</Text>
|
||||
<Text variant="medium">Gettings started in Cosmos DB</Text>
|
||||
{currentStep < 5 && (
|
||||
<Pivot style={{ marginTop: 10, width: "100%" }} selectedKey={GuideSteps[currentStep]}>
|
||||
<PivotItem
|
||||
@ -159,7 +110,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
<br />
|
||||
To begin, please enter the cluster's password in the PostgreSQL terminal.
|
||||
</Text>
|
||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
<Youtube videoId="UaBDXHMQAUw" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
@ -170,14 +121,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
>
|
||||
<Stack style={{ marginTop: 20 }}>
|
||||
<Text>Let’s create two tables github_users and github_events in “cosmosdb_tutorial” schema.</Text>
|
||||
<DefaultButton style={{ marginTop: 16, width: 150 }}>Create new table</DefaultButton>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 150 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(newTableCommand)}
|
||||
>
|
||||
Create new table
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="newTableCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={newTableCommand}
|
||||
defaultValue={newTableCommandForDisplay}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
@ -194,7 +150,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
onClick={() => onCopyBtnClicked("#newTableCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
<Youtube videoId="VJqupvSQ-mw" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
@ -210,14 +166,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
<br />
|
||||
We are choosing “user_id” as the distribution column for our sample dataset.
|
||||
</Text>
|
||||
<DefaultButton style={{ marginTop: 16, width: 200 }}>Create distributed table</DefaultButton>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 200 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(distributeTableCommand)}
|
||||
>
|
||||
Create distributed table
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="distributeTableCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={distributeTableCommand}
|
||||
defaultValue={distributeTableCommandForDisplay}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
@ -234,7 +195,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
onClick={() => onCopyBtnClicked("#distributeTableCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
<Youtube videoId="Q-AW7q1GLDY" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
@ -245,14 +206,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
>
|
||||
<Stack style={{ marginTop: 20 }}>
|
||||
<Text>Let's load the two tables with a sample dataset generated from the GitHub API.</Text>
|
||||
<DefaultButton style={{ marginTop: 16, width: 110 }}>Load data</DefaultButton>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 110 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(loadDataCommand)}
|
||||
>
|
||||
Load data
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="loadDataCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={loadDataCommand}
|
||||
defaultValue={loadDataCommandForDisplay}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
@ -269,7 +235,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
onClick={() => onCopyBtnClicked("#loadDataCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
<Youtube videoId="h15fvLKXzRo" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
@ -282,14 +248,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
<Text>
|
||||
Congratulations on creating and distributing your tables. Now, it's time to run your first query!
|
||||
</Text>
|
||||
<DefaultButton style={{ marginTop: 16, width: 115 }}>Try queries</DefaultButton>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 115 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(queryCommand)}
|
||||
>
|
||||
Try queries
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="queryCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={queryCommand}
|
||||
defaultValue={queryCommandForDisplay}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
@ -306,7 +277,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
||||
onClick={() => onCopyBtnClicked("#queryCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
<Youtube videoId="p46nRnE4b8Y" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
|
@ -329,7 +329,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
iconSrc: PowerShellIcon,
|
||||
title: "PostgreSQL Shell",
|
||||
description: "Create table and interact with data using PostgreSQL’s shell interface",
|
||||
onClick: () => this.container.openNotebookTerminal(TerminalKind.Mongo),
|
||||
onClick: () => this.container.openNotebookTerminal(TerminalKind.Postgres),
|
||||
};
|
||||
heroes.push(postgreShellBtn);
|
||||
} else {
|
||||
|
@ -18,7 +18,7 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
|
||||
}, []);
|
||||
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
|
||||
authToken: notebookServerInfo.authToken,
|
||||
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/mongo`,
|
||||
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/postgresql`,
|
||||
forwardingId: notebookServerInfo.forwardingId,
|
||||
});
|
||||
|
||||
@ -32,7 +32,7 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
|
||||
<NotebookTerminalComponent
|
||||
notebookServerInfo={getNotebookServerInfo()}
|
||||
databaseAccount={userContext.databaseAccount}
|
||||
tabId="EmbbedTerminal"
|
||||
tabId="QuickstartPSQLShell"
|
||||
/>
|
||||
)}
|
||||
{!notebookServerInfo?.notebookServerEndpoint && (
|
||||
|
@ -2,7 +2,7 @@
|
||||
* JupyterLab applications based on jupyterLab components
|
||||
*/
|
||||
import { ServerConnection, TerminalManager } from "@jupyterlab/services";
|
||||
import { IMessage } from "@jupyterlab/services/lib/terminal/terminal";
|
||||
import { IMessage, ITerminalConnection } from "@jupyterlab/services/lib/terminal/terminal";
|
||||
import { Terminal } from "@jupyterlab/terminal";
|
||||
import { Panel, Widget } from "@phosphor/widgets";
|
||||
import { userContext } from "UserContext";
|
||||
@ -46,7 +46,7 @@ export class JupyterLabAppFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public async createTerminalApp(serverSettings: ServerConnection.ISettings) {
|
||||
public async createTerminalApp(serverSettings: ServerConnection.ISettings): Promise<ITerminalConnection | undefined> {
|
||||
const manager = new TerminalManager({
|
||||
serverSettings: serverSettings,
|
||||
});
|
||||
@ -68,7 +68,7 @@ export class JupyterLabAppFactory {
|
||||
|
||||
if (!term) {
|
||||
console.error("Failed starting terminal");
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
term.title.closable = false;
|
||||
@ -90,5 +90,7 @@ export class JupyterLabAppFactory {
|
||||
window.addEventListener("unload", () => {
|
||||
panel.dispose();
|
||||
});
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ServerConnection } from "@jupyterlab/services";
|
||||
import { IMessage, ITerminalConnection } from "@jupyterlab/services/lib/terminal/terminal";
|
||||
import "@jupyterlab/terminal/style/index.css";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import postRobot from "post-robot";
|
||||
@ -41,7 +42,7 @@ const createServerSettings = (props: TerminalProps): ServerConnection.ISettings
|
||||
return ServerConnection.makeSettings(options);
|
||||
};
|
||||
|
||||
const initTerminal = async (props: TerminalProps) => {
|
||||
const initTerminal = async (props: TerminalProps): Promise<ITerminalConnection | undefined> => {
|
||||
// Initialize userContext (only properties which are needed by TelemetryProcessor)
|
||||
updateUserContext({
|
||||
subscriptionId: props.subscriptionId,
|
||||
@ -55,10 +56,12 @@ const initTerminal = async (props: TerminalProps) => {
|
||||
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
|
||||
|
||||
try {
|
||||
await new JupyterLabAppFactory(() => closeTab(props.tabId)).createTerminalApp(serverSettings);
|
||||
const session = await new JupyterLabAppFactory(() => closeTab(props.tabId)).createTerminalApp(serverSettings);
|
||||
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
|
||||
return session;
|
||||
} catch (error) {
|
||||
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@ -70,6 +73,7 @@ const closeTab = (tabId: string): void => {
|
||||
};
|
||||
|
||||
const main = async (): Promise<void> => {
|
||||
let session: ITerminalConnection | undefined;
|
||||
postRobot.on(
|
||||
"props",
|
||||
{
|
||||
@ -80,7 +84,22 @@ const main = async (): Promise<void> => {
|
||||
// Typescript definition for event is wrong. So read props by casting to <any>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const props = (event as any).data as TerminalProps;
|
||||
await initTerminal(props);
|
||||
session = await initTerminal(props);
|
||||
}
|
||||
);
|
||||
|
||||
postRobot.on(
|
||||
"sendMessage",
|
||||
{
|
||||
window: window.parent,
|
||||
domain: window.location.origin,
|
||||
},
|
||||
async (event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const message = (event as any).data as IMessage;
|
||||
if (session) {
|
||||
session.send(message);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||
import { AuthType } from "../AuthType";
|
||||
@ -100,7 +100,11 @@ async function configureHosted(): Promise<Explorer> {
|
||||
}
|
||||
|
||||
if (event.data?.type === MessageTypes.CloseTab) {
|
||||
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
|
||||
if (event.data?.data?.tabId === "QuickstartPSQLShell") {
|
||||
useTabs.getState().closeReactTab(ReactTabKind.Quickstart);
|
||||
} else {
|
||||
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
|
||||
}
|
||||
}
|
||||
},
|
||||
false
|
||||
@ -290,7 +294,11 @@ async function configurePortal(): Promise<Explorer> {
|
||||
} else if (shouldForwardMessage(message, event.origin)) {
|
||||
sendMessage(message);
|
||||
} else if (event.data?.type === MessageTypes.CloseTab) {
|
||||
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
|
||||
if (event.data?.data?.tabId === "QuickstartPSQLShell") {
|
||||
useTabs.getState().closeReactTab(ReactTabKind.Quickstart);
|
||||
} else {
|
||||
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
|
||||
}
|
||||
}
|
||||
},
|
||||
false
|
||||
|
26
src/hooks/useTerminal.ts
Normal file
26
src/hooks/useTerminal.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import postRobot from "post-robot";
|
||||
import create, { UseStore } from "zustand";
|
||||
|
||||
interface TerminalState {
|
||||
terminalWindow: Window;
|
||||
setTerminal: (terminalWindow: Window) => void;
|
||||
sendMessage: (message: string) => void;
|
||||
}
|
||||
|
||||
export const useTerminal: UseStore<TerminalState> = create((set, get) => ({
|
||||
terminalWindow: undefined,
|
||||
setTerminal: (terminalWindow: Window) => {
|
||||
set({ terminalWindow });
|
||||
},
|
||||
sendMessage: (message: string) => {
|
||||
const terminalWindow = get().terminalWindow;
|
||||
postRobot.send(
|
||||
terminalWindow,
|
||||
"sendMessage",
|
||||
{ type: "stdin", content: [message] },
|
||||
{
|
||||
domain: window.location.origin,
|
||||
}
|
||||
);
|
||||
},
|
||||
}));
|
Loading…
Reference in New Issue
Block a user