mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-16 17:25:58 +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[];
|
privateEndpointConnections?: unknown[];
|
||||||
capacity?: { totalThroughputLimit: number };
|
capacity?: { totalThroughputLimit: number };
|
||||||
locations?: DatabaseAccountResponseLocation[];
|
locations?: DatabaseAccountResponseLocation[];
|
||||||
|
postgresqlEndpoint?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseAccountResponseLocation {
|
export interface DatabaseAccountResponseLocation {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* Wrapper around Notebook server terminal
|
* Wrapper around Notebook server terminal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useTerminal } from "hooks/useTerminal";
|
||||||
import postRobot from "post-robot";
|
import postRobot from "post-robot";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
@ -40,6 +41,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
|||||||
|
|
||||||
handleFrameLoad(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {
|
handleFrameLoad(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {
|
||||||
this.terminalWindow = (event.target as HTMLIFrameElement).contentWindow;
|
this.terminalWindow = (event.target as HTMLIFrameElement).contentWindow;
|
||||||
|
useTerminal.getState().setTerminal(this.terminalWindow);
|
||||||
this.sendPropsToTerminalFrame();
|
this.sendPropsToTerminalFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
|||||||
} 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")) {
|
} else if (StringUtils.endsWith(notebookServerEndpoint, "postgresql")) {
|
||||||
return (this.props.databaseAccount?.properties as any).postgresqlEndpoint;
|
return this.props.databaseAccount?.properties.postgresqlEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (terminalEndpoint) {
|
if (terminalEndpoint) {
|
||||||
|
@ -186,9 +186,7 @@ export default class Explorer {
|
|||||||
useNotebook.getState().setNotebookBasePath(userContext.features.notebookBasePath);
|
useNotebook.getState().setNotebookBasePath(userContext.features.notebookBasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.apiType !== "Postgres") {
|
this.refreshExplorer();
|
||||||
this.refreshExplorer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initiateAndRefreshNotebookList(): Promise<void> {
|
public async initiateAndRefreshNotebookList(): Promise<void> {
|
||||||
@ -1249,9 +1247,11 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refreshExplorer(): Promise<void> {
|
public async refreshExplorer(): Promise<void> {
|
||||||
userContext.authType === AuthType.ResourceToken
|
if (userContext.apiType !== "Postgres") {
|
||||||
? this.refreshDatabaseForResourceToken()
|
userContext.authType === AuthType.ResourceToken
|
||||||
: this.refreshAllDatabases();
|
? this.refreshDatabaseForResourceToken()
|
||||||
|
: this.refreshAllDatabases();
|
||||||
|
}
|
||||||
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
||||||
|
|
||||||
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
||||||
|
@ -85,14 +85,11 @@ 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));
|
||||||
}
|
}
|
||||||
@ -612,16 +609,7 @@ function createStaticCommandBarButtonsForResourceToken(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const postgreShellLabel = "Open PostgreSQL Shell";
|
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
|
||||||
const openPostgreShellBtn = {
|
|
||||||
iconSrc: HostedTerminalIcon,
|
|
||||||
iconAlt: postgreShellLabel,
|
|
||||||
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Mongo),
|
|
||||||
commandButtonLabel: postgreShellLabel,
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: false,
|
|
||||||
ariaLabel: postgreShellLabel,
|
|
||||||
};
|
|
||||||
|
|
||||||
return [openPostgreShellBtn];
|
return [openPostgreShellBtn];
|
||||||
}
|
}
|
||||||
|
@ -124,8 +124,9 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const firstWriteLocation =
|
const firstWriteLocation =
|
||||||
databaseAccount?.properties?.writeLocations &&
|
userContext.apiType === "Postgres"
|
||||||
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
|
? databaseAccount?.location
|
||||||
|
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
|
||||||
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
||||||
const authorizationHeader = getAuthorizationHeader();
|
const authorizationHeader = getAuthorizationHeader();
|
||||||
try {
|
try {
|
||||||
@ -313,7 +314,10 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
|||||||
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {
|
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {
|
||||||
if (dbAccountAllowedInfo?.type === PhoenixErrorType.PhoenixFlightFallback) {
|
if (dbAccountAllowedInfo?.type === PhoenixErrorType.PhoenixFlightFallback) {
|
||||||
isPhoenixNotebooks = isPublicInternetAllowed && userContext.features.phoenixNotebooks === true;
|
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 {
|
} else {
|
||||||
isPhoenixNotebooks = isPhoenixFeatures = isPublicInternetAllowed;
|
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,
|
Text,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@fluentui/react";
|
} 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 React, { useState } from "react";
|
||||||
import Youtube from "react-youtube";
|
import Youtube from "react-youtube";
|
||||||
import Pivot1SelectedIcon from "../../../images/Pivot1_selected.svg";
|
import Pivot1SelectedIcon from "../../../images/Pivot1_selected.svg";
|
||||||
@ -35,65 +46,6 @@ enum GuideSteps {
|
|||||||
|
|
||||||
export const QuickstartGuide: React.FC = (): JSX.Element => {
|
export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||||
const [currentStep, setCurrentStep] = useState<number>(0);
|
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 onCopyBtnClicked = (selector: string): void => {
|
||||||
const textfield: HTMLInputElement = document.querySelector(selector);
|
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={{ paddingTop: 8, height: "100%", width: "100%" }}>
|
||||||
<Stack style={{ flexGrow: 1, padding: "0 20px", overflow: "auto" }}>
|
<Stack style={{ flexGrow: 1, padding: "0 20px", overflow: "auto" }}>
|
||||||
<Text variant="xxLarge">Quick start guide</Text>
|
<Text variant="xxLarge">Quick start guide</Text>
|
||||||
<Text variant="medium">Gettings started in Cosmos DB</Text>
|
|
||||||
{currentStep < 5 && (
|
{currentStep < 5 && (
|
||||||
<Pivot style={{ marginTop: 10, width: "100%" }} selectedKey={GuideSteps[currentStep]}>
|
<Pivot style={{ marginTop: 10, width: "100%" }} selectedKey={GuideSteps[currentStep]}>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
@ -159,7 +110,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
<br />
|
<br />
|
||||||
To begin, please enter the cluster's password in the PostgreSQL terminal.
|
To begin, please enter the cluster's password in the PostgreSQL terminal.
|
||||||
</Text>
|
</Text>
|
||||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
<Youtube videoId="UaBDXHMQAUw" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
@ -170,14 +121,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
>
|
>
|
||||||
<Stack style={{ marginTop: 20 }}>
|
<Stack style={{ marginTop: 20 }}>
|
||||||
<Text>Let’s create two tables github_users and github_events in “cosmosdb_tutorial” schema.</Text>
|
<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 }}>
|
<Stack horizontal style={{ marginTop: 16 }}>
|
||||||
<TextField
|
<TextField
|
||||||
id="newTableCommand"
|
id="newTableCommand"
|
||||||
multiline
|
multiline
|
||||||
rows={5}
|
rows={5}
|
||||||
readOnly
|
readOnly
|
||||||
defaultValue={newTableCommand}
|
defaultValue={newTableCommandForDisplay}
|
||||||
styles={{
|
styles={{
|
||||||
root: { width: "90%" },
|
root: { width: "90%" },
|
||||||
field: {
|
field: {
|
||||||
@ -194,7 +150,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
onClick={() => onCopyBtnClicked("#newTableCommand")}
|
onClick={() => onCopyBtnClicked("#newTableCommand")}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
<Youtube videoId="VJqupvSQ-mw" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
@ -210,14 +166,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
<br />
|
<br />
|
||||||
We are choosing “user_id” as the distribution column for our sample dataset.
|
We are choosing “user_id” as the distribution column for our sample dataset.
|
||||||
</Text>
|
</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 }}>
|
<Stack horizontal style={{ marginTop: 16 }}>
|
||||||
<TextField
|
<TextField
|
||||||
id="distributeTableCommand"
|
id="distributeTableCommand"
|
||||||
multiline
|
multiline
|
||||||
rows={5}
|
rows={5}
|
||||||
readOnly
|
readOnly
|
||||||
defaultValue={distributeTableCommand}
|
defaultValue={distributeTableCommandForDisplay}
|
||||||
styles={{
|
styles={{
|
||||||
root: { width: "90%" },
|
root: { width: "90%" },
|
||||||
field: {
|
field: {
|
||||||
@ -234,7 +195,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
onClick={() => onCopyBtnClicked("#distributeTableCommand")}
|
onClick={() => onCopyBtnClicked("#distributeTableCommand")}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
<Youtube videoId="Q-AW7q1GLDY" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
@ -245,14 +206,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
>
|
>
|
||||||
<Stack style={{ marginTop: 20 }}>
|
<Stack style={{ marginTop: 20 }}>
|
||||||
<Text>Let's load the two tables with a sample dataset generated from the GitHub API.</Text>
|
<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 }}>
|
<Stack horizontal style={{ marginTop: 16 }}>
|
||||||
<TextField
|
<TextField
|
||||||
id="loadDataCommand"
|
id="loadDataCommand"
|
||||||
multiline
|
multiline
|
||||||
rows={5}
|
rows={5}
|
||||||
readOnly
|
readOnly
|
||||||
defaultValue={loadDataCommand}
|
defaultValue={loadDataCommandForDisplay}
|
||||||
styles={{
|
styles={{
|
||||||
root: { width: "90%" },
|
root: { width: "90%" },
|
||||||
field: {
|
field: {
|
||||||
@ -269,7 +235,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
onClick={() => onCopyBtnClicked("#loadDataCommand")}
|
onClick={() => onCopyBtnClicked("#loadDataCommand")}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
<Youtube videoId="h15fvLKXzRo" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
@ -282,14 +248,19 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
<Text>
|
<Text>
|
||||||
Congratulations on creating and distributing your tables. Now, it's time to run your first query!
|
Congratulations on creating and distributing your tables. Now, it's time to run your first query!
|
||||||
</Text>
|
</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 }}>
|
<Stack horizontal style={{ marginTop: 16 }}>
|
||||||
<TextField
|
<TextField
|
||||||
id="queryCommand"
|
id="queryCommand"
|
||||||
multiline
|
multiline
|
||||||
rows={5}
|
rows={5}
|
||||||
readOnly
|
readOnly
|
||||||
defaultValue={queryCommand}
|
defaultValue={queryCommandForDisplay}
|
||||||
styles={{
|
styles={{
|
||||||
root: { width: "90%" },
|
root: { width: "90%" },
|
||||||
field: {
|
field: {
|
||||||
@ -306,7 +277,7 @@ SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::
|
|||||||
onClick={() => onCopyBtnClicked("#queryCommand")}
|
onClick={() => onCopyBtnClicked("#queryCommand")}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
<Youtube videoId="p46nRnE4b8Y" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
</Pivot>
|
</Pivot>
|
||||||
|
@ -329,7 +329,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
iconSrc: PowerShellIcon,
|
iconSrc: PowerShellIcon,
|
||||||
title: "PostgreSQL Shell",
|
title: "PostgreSQL Shell",
|
||||||
description: "Create table and interact with data using PostgreSQL’s shell interface",
|
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);
|
heroes.push(postgreShellBtn);
|
||||||
} else {
|
} else {
|
||||||
|
@ -18,7 +18,7 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
|
|||||||
}, []);
|
}, []);
|
||||||
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
|
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
|
||||||
authToken: notebookServerInfo.authToken,
|
authToken: notebookServerInfo.authToken,
|
||||||
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/mongo`,
|
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/postgresql`,
|
||||||
forwardingId: notebookServerInfo.forwardingId,
|
forwardingId: notebookServerInfo.forwardingId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
|
|||||||
<NotebookTerminalComponent
|
<NotebookTerminalComponent
|
||||||
notebookServerInfo={getNotebookServerInfo()}
|
notebookServerInfo={getNotebookServerInfo()}
|
||||||
databaseAccount={userContext.databaseAccount}
|
databaseAccount={userContext.databaseAccount}
|
||||||
tabId="EmbbedTerminal"
|
tabId="QuickstartPSQLShell"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!notebookServerInfo?.notebookServerEndpoint && (
|
{!notebookServerInfo?.notebookServerEndpoint && (
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* JupyterLab applications based on jupyterLab components
|
* JupyterLab applications based on jupyterLab components
|
||||||
*/
|
*/
|
||||||
import { ServerConnection, TerminalManager } from "@jupyterlab/services";
|
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 { Terminal } from "@jupyterlab/terminal";
|
||||||
import { Panel, Widget } from "@phosphor/widgets";
|
import { Panel, Widget } from "@phosphor/widgets";
|
||||||
import { userContext } from "UserContext";
|
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({
|
const manager = new TerminalManager({
|
||||||
serverSettings: serverSettings,
|
serverSettings: serverSettings,
|
||||||
});
|
});
|
||||||
@ -68,7 +68,7 @@ export class JupyterLabAppFactory {
|
|||||||
|
|
||||||
if (!term) {
|
if (!term) {
|
||||||
console.error("Failed starting terminal");
|
console.error("Failed starting terminal");
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
term.title.closable = false;
|
term.title.closable = false;
|
||||||
@ -90,5 +90,7 @@ export class JupyterLabAppFactory {
|
|||||||
window.addEventListener("unload", () => {
|
window.addEventListener("unload", () => {
|
||||||
panel.dispose();
|
panel.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return session;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ServerConnection } from "@jupyterlab/services";
|
import { ServerConnection } from "@jupyterlab/services";
|
||||||
|
import { IMessage, ITerminalConnection } from "@jupyterlab/services/lib/terminal/terminal";
|
||||||
import "@jupyterlab/terminal/style/index.css";
|
import "@jupyterlab/terminal/style/index.css";
|
||||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
import postRobot from "post-robot";
|
import postRobot from "post-robot";
|
||||||
@ -41,7 +42,7 @@ const createServerSettings = (props: TerminalProps): ServerConnection.ISettings
|
|||||||
return ServerConnection.makeSettings(options);
|
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)
|
// Initialize userContext (only properties which are needed by TelemetryProcessor)
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
subscriptionId: props.subscriptionId,
|
subscriptionId: props.subscriptionId,
|
||||||
@ -55,10 +56,12 @@ const initTerminal = async (props: TerminalProps) => {
|
|||||||
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
|
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
|
||||||
|
|
||||||
try {
|
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);
|
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
|
||||||
|
return session;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);
|
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,6 +73,7 @@ const closeTab = (tabId: string): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const main = async (): Promise<void> => {
|
const main = async (): Promise<void> => {
|
||||||
|
let session: ITerminalConnection | undefined;
|
||||||
postRobot.on(
|
postRobot.on(
|
||||||
"props",
|
"props",
|
||||||
{
|
{
|
||||||
@ -80,7 +84,22 @@ const main = async (): Promise<void> => {
|
|||||||
// Typescript definition for event is wrong. So read props by casting to <any>
|
// Typescript definition for event is wrong. So read props by casting to <any>
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const props = (event as any).data as TerminalProps;
|
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 { useEffect, useState } from "react";
|
||||||
import { applyExplorerBindings } from "../applyExplorerBindings";
|
import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
@ -100,7 +100,11 @@ async function configureHosted(): Promise<Explorer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.data?.type === MessageTypes.CloseTab) {
|
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
|
false
|
||||||
@ -290,7 +294,11 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
} else if (shouldForwardMessage(message, event.origin)) {
|
} else if (shouldForwardMessage(message, event.origin)) {
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
} else if (event.data?.type === MessageTypes.CloseTab) {
|
} 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
|
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…
x
Reference in New Issue
Block a user