mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 01:11:25 +00:00
Adding vcore mongo quickstart (#1600)
* Safety checkin * Adding vcoremongo to Main * Safety checkin * Adding vcoremongo to Main * Safety commit * Safety checkin * Adding vcoremongo to Main * Safety commit * Integrating mongo shell * Safety checkin * Adding vcoremongo to Main * Safety commit * Integrating mongo shell * Safety checkin * Safety commit * Enable mongo shell in its own tab * Safety checkin * Adding vcoremongo to Main * Safety commit * Integrating mongo shell * Safety checkin * Safety commit * Safety commit * Integrating mongo shell * Safety checkin * Safety commit * Enable mongo shell in its own tab * Adding message * Integrated mongo shell * Moving Juno endpoint back to prod * Fixed command bar unit tests * Fixing spelling
This commit is contained in:
@@ -14,6 +14,7 @@ export interface NotebookTerminalComponentProps {
|
||||
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
||||
databaseAccount: DataModels.DatabaseAccount;
|
||||
tabId: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
export class NotebookTerminalComponent extends React.Component<NotebookTerminalComponentProps> {
|
||||
@@ -50,7 +51,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
return;
|
||||
}
|
||||
|
||||
const props: TerminalProps = {
|
||||
let props: TerminalProps = {
|
||||
terminalEndpoint: this.tryGetTerminalEndpoint(),
|
||||
notebookServerEndpoint: this.props.notebookServerInfo?.notebookServerEndpoint,
|
||||
authToken: this.props.notebookServerInfo?.authToken,
|
||||
@@ -61,6 +62,13 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
tabId: this.props.tabId,
|
||||
};
|
||||
|
||||
if (this.props.username) {
|
||||
props = {
|
||||
...props,
|
||||
username: this.props.username,
|
||||
};
|
||||
}
|
||||
|
||||
postRobot.send(this.terminalWindow, "props", props, {
|
||||
domain: window.location.origin,
|
||||
});
|
||||
@@ -78,6 +86,8 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
terminalEndpoint = this.props.databaseAccount?.properties.cassandraEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "postgresql")) {
|
||||
return this.props.databaseAccount?.properties.postgresqlEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "mongovcore")) {
|
||||
return this.props.databaseAccount?.properties.vcoreMongoEndpoint;
|
||||
}
|
||||
|
||||
if (terminalEndpoint) {
|
||||
|
||||
@@ -1127,6 +1127,10 @@ export default class Explorer {
|
||||
title = "PSQL Shell";
|
||||
break;
|
||||
|
||||
case ViewModels.TerminalKind.VCoreMongo:
|
||||
title = "VCoreMongo Shell";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Terminal kind: ${kind} not supported");
|
||||
}
|
||||
@@ -1313,7 +1317,7 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public async refreshExplorer(): Promise<void> {
|
||||
if (userContext.apiType !== "Postgres") {
|
||||
if (userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo") {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
: this.refreshAllDatabases();
|
||||
|
||||
@@ -34,8 +34,11 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
const buttons = useCommandBar((state) => state.contextButtons);
|
||||
const backgroundColor = StyleConstants.BaseLight;
|
||||
|
||||
if (userContext.apiType === "Postgres") {
|
||||
const buttons = CommandBarComponentButtonFactory.createPostgreButtons(container);
|
||||
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
|
||||
const buttons =
|
||||
userContext.apiType === "Postgres"
|
||||
? CommandBarComponentButtonFactory.createPostgreButtons(container)
|
||||
: CommandBarComponentButtonFactory.createVCoreMongoButtons(container);
|
||||
return (
|
||||
<div className="commandBarContainer">
|
||||
<FluentCommandBar
|
||||
|
||||
@@ -142,8 +142,8 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Open Mongo Shell button", () => {
|
||||
const openMongoShellBtnLabel = "Open Mongo Shell";
|
||||
describe("Open Mongo shell button", () => {
|
||||
const openMongoShellBtnLabel = "Open Mongo shell";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -247,8 +247,8 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Open Cassandra Shell button", () => {
|
||||
const openCassandraShellBtnLabel = "Open Cassandra Shell";
|
||||
describe("Open Cassandra shell button", () => {
|
||||
const openCassandraShellBtnLabel = "Open Cassandra shell";
|
||||
const selectedNodeState = useSelectedNode.getState();
|
||||
|
||||
beforeAll(() => {
|
||||
|
||||
@@ -94,9 +94,9 @@ export function createStaticCommandBarButtons(
|
||||
) {
|
||||
notebookButtons.push(createDivider());
|
||||
if (userContext.apiType === "Cassandra") {
|
||||
notebookButtons.push(createOpenCassandraTerminalButton(container));
|
||||
notebookButtons.push(createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.Cassandra));
|
||||
} else {
|
||||
notebookButtons.push(createOpenMongoTerminalButton(container));
|
||||
notebookButtons.push(createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.Mongo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,8 +499,25 @@ function createOpenTerminalButton(container: Explorer): CommandButtonComponentPr
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenMongoTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open Mongo Shell";
|
||||
function createOpenTerminalButtonByKind(
|
||||
container: Explorer,
|
||||
terminalKind: ViewModels.TerminalKind
|
||||
): CommandButtonComponentProps {
|
||||
const terminalFriendlyName = (): string => {
|
||||
switch (terminalKind) {
|
||||
case ViewModels.TerminalKind.Cassandra:
|
||||
return "Cassandra";
|
||||
case ViewModels.TerminalKind.Mongo:
|
||||
return "Mongo";
|
||||
case ViewModels.TerminalKind.Postgres:
|
||||
return "PSQL";
|
||||
case ViewModels.TerminalKind.VCoreMongo:
|
||||
return "MongoDB (vcore)";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
const label = `Open ${terminalFriendlyName()} shell`;
|
||||
const tooltip =
|
||||
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
||||
const disableButton =
|
||||
@@ -510,7 +527,7 @@ function createOpenMongoTerminalButton(container: Explorer): CommandButtonCompon
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
if (useNotebook.getState().isNotebookEnabled) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||
container.openNotebookTerminal(terminalKind);
|
||||
}
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
@@ -521,51 +538,6 @@ function createOpenMongoTerminalButton(container: Explorer): CommandButtonCompon
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenCassandraTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open Cassandra Shell";
|
||||
const tooltip =
|
||||
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
||||
const disableButton =
|
||||
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||
return {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
if (useNotebook.getState().isNotebookEnabled) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
||||
}
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: disableButton,
|
||||
ariaLabel: label,
|
||||
tooltipText: !disableButton ? "" : tooltip,
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenPsqlTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open PSQL Shell";
|
||||
const disableButton =
|
||||
(!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled) ||
|
||||
useSelectedNode.getState().isQueryCopilotCollectionSelected();
|
||||
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 {
|
||||
const label = "Reset Workspace";
|
||||
return {
|
||||
@@ -630,7 +602,13 @@ function createStaticCommandBarButtonsForResourceToken(
|
||||
}
|
||||
|
||||
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
|
||||
const openPostgreShellBtn = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.Postgres);
|
||||
|
||||
return [openPostgreShellBtn];
|
||||
}
|
||||
|
||||
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
||||
|
||||
return [openVCoreMongoTerminalButton];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { PhoenixClient } from "Phoenix/PhoenixClient";
|
||||
import { cloneDeep } from "lodash";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
@@ -10,13 +10,13 @@ import * as Logger from "../../Common/Logger";
|
||||
import { configContext } from "../../ConfigContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo, ContainerInfo, PhoenixErrorType } from "../../Contracts/DataModels";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
||||
import NotebookManager from "./NotebookManager";
|
||||
|
||||
@@ -124,7 +124,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
}
|
||||
|
||||
const firstWriteLocation =
|
||||
userContext.apiType === "Postgres"
|
||||
userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo"
|
||||
? databaseAccount?.location
|
||||
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
|
||||
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
||||
@@ -316,8 +316,10 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
isPhoenixNotebooks = isPublicInternetAllowed && userContext.features.phoenixNotebooks === 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");
|
||||
// phoenix needs to be enabled for Postgres and VCoreMongo accounts since the PSQL and mongo shell requires phoenix containers
|
||||
(userContext.features.phoenixFeatures === true ||
|
||||
userContext.apiType === "Postgres" ||
|
||||
userContext.apiType === "VCoreMongo");
|
||||
} else {
|
||||
isPhoenixNotebooks = isPhoenixFeatures = isPublicInternetAllowed;
|
||||
}
|
||||
|
||||
@@ -2,20 +2,26 @@ import { Image, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import { sendMessage } from "Common/MessageHandler";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import React from "react";
|
||||
import FirewallRuleScreenshot from "../../../images/firewallRule.png";
|
||||
|
||||
export const QuickstartFirewallNotification: React.FC = (): JSX.Element => (
|
||||
export interface QuickstartFirewallNotificationProps {
|
||||
shellName: string;
|
||||
screenshot: string;
|
||||
messageType: MessageTypes;
|
||||
}
|
||||
|
||||
export const QuickstartFirewallNotification: React.FC<QuickstartFirewallNotificationProps> = ({
|
||||
shellName,
|
||||
screenshot,
|
||||
messageType,
|
||||
}: QuickstartFirewallNotificationProps): JSX.Element => (
|
||||
<Stack style={{ padding: "16px 20px" }}>
|
||||
<Text block>
|
||||
To use the PostgreSQL shell, you need to add a firewall rule to allow access from all IP addresses
|
||||
To use the {shellName} shell, you need to add a firewall rule to allow access from all IP addresses
|
||||
(0.0.0.0-255.255.255).
|
||||
</Text>
|
||||
<Text block>We strongly recommend removing this rule once you finish using the PostgreSQL shell.</Text>
|
||||
<Image style={{ margin: "20px 0" }} src={FirewallRuleScreenshot} />
|
||||
<PrimaryButton
|
||||
style={{ width: 150 }}
|
||||
onClick={() => sendMessage({ type: MessageTypes.OpenPostgresNetworkingBlade })}
|
||||
>
|
||||
<Text block>We strongly recommend removing this rule once you finish using the {shellName} shell.</Text>
|
||||
<Image style={{ margin: "20px 0" }} src={screenshot} />
|
||||
<PrimaryButton style={{ width: 150 }} onClick={() => sendMessage({ type: messageType })}>
|
||||
Add firewall rule
|
||||
</PrimaryButton>
|
||||
</Stack>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import {
|
||||
DefaultButton,
|
||||
Icon,
|
||||
IconButton,
|
||||
Image,
|
||||
IPivotItemProps,
|
||||
Pivot,
|
||||
PivotItem,
|
||||
PrimaryButton,
|
||||
@@ -21,18 +19,10 @@ import {
|
||||
queryCommand,
|
||||
queryCommandForDisplay,
|
||||
} from "Explorer/Quickstart/PostgreQuickstartCommands";
|
||||
import { customPivotHeaderRenderer } from "Explorer/Quickstart/Shared/QuickstartRenderUtilities";
|
||||
import { useTerminal } from "hooks/useTerminal";
|
||||
import React, { useState } from "react";
|
||||
import Youtube from "react-youtube";
|
||||
import Pivot1SelectedIcon from "../../../images/Pivot1_selected.svg";
|
||||
import Pivot2Icon from "../../../images/Pivot2.svg";
|
||||
import Pivot2SelectedIcon from "../../../images/Pivot2_selected.svg";
|
||||
import Pivot3Icon from "../../../images/Pivot3.svg";
|
||||
import Pivot3SelectedIcon from "../../../images/Pivot3_selected.svg";
|
||||
import Pivot4Icon from "../../../images/Pivot4.svg";
|
||||
import Pivot4SelectedIcon from "../../../images/Pivot4_selected.svg";
|
||||
import Pivot5Icon from "../../../images/Pivot5.svg";
|
||||
import Pivot5SelectedIcon from "../../../images/Pivot5_selected.svg";
|
||||
import CompleteIcon from "../../../images/QuickstartComplete.svg";
|
||||
import { ReactTabKind, useTabs } from "../../hooks/useTabs";
|
||||
|
||||
@@ -53,44 +43,6 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
const getPivotHeaderIcon = (step: number): string => {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return Pivot1SelectedIcon;
|
||||
case 1:
|
||||
return step === currentStep ? Pivot2SelectedIcon : Pivot2Icon;
|
||||
case 2:
|
||||
return step === currentStep ? Pivot3SelectedIcon : Pivot3Icon;
|
||||
case 3:
|
||||
return step === currentStep ? Pivot4SelectedIcon : Pivot4Icon;
|
||||
case 4:
|
||||
return step === currentStep ? Pivot5SelectedIcon : Pivot5Icon;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const customPivotHeaderRenderer = (
|
||||
link: IPivotItemProps,
|
||||
defaultRenderer: (link?: IPivotItemProps) => JSX.Element | null,
|
||||
step: number
|
||||
): JSX.Element | null => {
|
||||
if (!link || !defaultRenderer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack horizontal verticalAlign="center">
|
||||
{currentStep > step ? (
|
||||
<Icon iconName="CompletedSolid" style={{ color: "#57A300", marginRight: 8 }} />
|
||||
) : (
|
||||
<Image style={{ marginRight: 8 }} src={getPivotHeaderIcon(step)} />
|
||||
)}
|
||||
{defaultRenderer({ ...link, itemIcon: undefined })}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack style={{ paddingTop: 8, height: "100%", width: "100%" }}>
|
||||
<Stack style={{ flexGrow: 1, padding: "0 20px", overflow: "auto" }}>
|
||||
@@ -103,7 +55,9 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
>
|
||||
<PivotItem
|
||||
headerText="Login"
|
||||
onRenderItemLink={(props, defaultRenderer) => customPivotHeaderRenderer(props, defaultRenderer, 0)}
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 0)
|
||||
}
|
||||
itemKey={GuideSteps[0]}
|
||||
onClick={() => {
|
||||
setCurrentStep(0);
|
||||
@@ -125,7 +79,9 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="New table"
|
||||
onRenderItemLink={(props, defaultRenderer) => customPivotHeaderRenderer(props, defaultRenderer, 1)}
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 1)
|
||||
}
|
||||
itemKey={GuideSteps[1]}
|
||||
onClick={() => setCurrentStep(1)}
|
||||
>
|
||||
@@ -165,7 +121,9 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Distribute table"
|
||||
onRenderItemLink={(props, defaultRenderer) => customPivotHeaderRenderer(props, defaultRenderer, 2)}
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 2)
|
||||
}
|
||||
itemKey={GuideSteps[2]}
|
||||
onClick={() => setCurrentStep(2)}
|
||||
>
|
||||
@@ -210,7 +168,9 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Load data"
|
||||
onRenderItemLink={(props, defaultRenderer) => customPivotHeaderRenderer(props, defaultRenderer, 3)}
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 3)
|
||||
}
|
||||
itemKey={GuideSteps[3]}
|
||||
onClick={() => setCurrentStep(3)}
|
||||
>
|
||||
@@ -250,7 +210,9 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Query"
|
||||
onRenderItemLink={(props, defaultRenderer) => customPivotHeaderRenderer(props, defaultRenderer, 4)}
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 4)
|
||||
}
|
||||
itemKey={GuideSteps[4]}
|
||||
onClick={() => setCurrentStep(4)}
|
||||
>
|
||||
|
||||
50
src/Explorer/Quickstart/Shared/QuickstartRenderUtilities.tsx
Normal file
50
src/Explorer/Quickstart/Shared/QuickstartRenderUtilities.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Icon, Image, IPivotItemProps, Stack } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import Pivot1SelectedIcon from "../../../../images/Pivot1_selected.svg";
|
||||
import Pivot2Icon from "../../../../images/Pivot2.svg";
|
||||
import Pivot2SelectedIcon from "../../../../images/Pivot2_selected.svg";
|
||||
import Pivot3Icon from "../../../../images/Pivot3.svg";
|
||||
import Pivot3SelectedIcon from "../../../../images/Pivot3_selected.svg";
|
||||
import Pivot4Icon from "../../../../images/Pivot4.svg";
|
||||
import Pivot4SelectedIcon from "../../../../images/Pivot4_selected.svg";
|
||||
import Pivot5Icon from "../../../../images/Pivot5.svg";
|
||||
import Pivot5SelectedIcon from "../../../../images/Pivot5_selected.svg";
|
||||
|
||||
const getPivotHeaderIcon = (currentStep: number, newStep: number): string => {
|
||||
switch (newStep) {
|
||||
case 0:
|
||||
return Pivot1SelectedIcon;
|
||||
case 1:
|
||||
return newStep === currentStep ? Pivot2SelectedIcon : Pivot2Icon;
|
||||
case 2:
|
||||
return newStep === currentStep ? Pivot3SelectedIcon : Pivot3Icon;
|
||||
case 3:
|
||||
return newStep === currentStep ? Pivot4SelectedIcon : Pivot4Icon;
|
||||
case 4:
|
||||
return newStep === currentStep ? Pivot5SelectedIcon : Pivot5Icon;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
export const customPivotHeaderRenderer = (
|
||||
link: IPivotItemProps,
|
||||
defaultRenderer: (link?: IPivotItemProps) => JSX.Element | null,
|
||||
currentStep: number,
|
||||
newStep: number
|
||||
): JSX.Element | null => {
|
||||
if (!link || !defaultRenderer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack horizontal verticalAlign="center">
|
||||
{currentStep > newStep ? (
|
||||
<Icon iconName="CompletedSolid" style={{ color: "#57A300", marginRight: 8 }} />
|
||||
) : (
|
||||
<Image style={{ marginRight: 8 }} src={getPivotHeaderIcon(currentStep, newStep)} />
|
||||
)}
|
||||
{defaultRenderer({ ...link, itemIcon: undefined })}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
34
src/Explorer/Quickstart/VCoreMongoQuickstartCommands.ts
Normal file
34
src/Explorer/Quickstart/VCoreMongoQuickstartCommands.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export const newDbAndCollectionCommand = `use quickstartDB
|
||||
db.createCollection('sampleCollection')`;
|
||||
|
||||
export const newDbAndCollectionCommandForDisplay = `use quickstartDB // Create new database named 'quickstartDB' or switch to it if it already exists
|
||||
|
||||
db.createCollection('sampleCollection') // Create new collection named 'sampleCollection'`;
|
||||
|
||||
export const loadDataCommand = `db.sampleCollection.insertMany([
|
||||
{title: "The Great Gatsby", author: "F. Scott Fitzgerald", pages: 180},
|
||||
{title: "To Kill a Mockingbird", author: "Harper Lee", pages: 324},
|
||||
{title: "1984", author: "George Orwell", pages: 328},
|
||||
{title: "The Catcher in the Rye", author: "J.D. Salinger", pages: 277},
|
||||
{title: "Moby-Dick", author: "Herman Melville", pages: 720},
|
||||
{title: "Pride and Prejudice", author: "Jane Austen", pages: 279},
|
||||
{title: "The Hobbit", author: "J.R.R. Tolkien", pages: 310},
|
||||
{title: "War and Peace", author: "Leo Tolstoy", pages: 1392},
|
||||
{title: "The Odyssey", author: "Homer", pages: 374},
|
||||
{title: "Ulysses", author: "James Joyce", pages: 730}
|
||||
])`;
|
||||
|
||||
export const queriesCommand = `db.sampleCollection.find({author: "George Orwell"})
|
||||
|
||||
db.sampleCollection.find({pages: {$gt: 500}})
|
||||
|
||||
db.sampleCollection.find({}).sort({pages: 1})`;
|
||||
|
||||
export const queriesCommandForDisplay = `// Query to find all books written by "George Orwell"
|
||||
db.sampleCollection.find({author: "George Orwell"})
|
||||
|
||||
// Query to find all books with more than 500 pages
|
||||
db.sampleCollection.find({pages: {$gt: 500}})
|
||||
|
||||
// Query to find all books and sort them by the number of pages in ascending order
|
||||
db.sampleCollection.find({}).sort({pages: 1})`;
|
||||
310
src/Explorer/Quickstart/VCoreMongoQuickstartGuide.tsx
Normal file
310
src/Explorer/Quickstart/VCoreMongoQuickstartGuide.tsx
Normal file
@@ -0,0 +1,310 @@
|
||||
import {
|
||||
DefaultButton,
|
||||
IconButton,
|
||||
Link,
|
||||
Pivot,
|
||||
PivotItem,
|
||||
PrimaryButton,
|
||||
Stack,
|
||||
Text,
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import { sendMessage } from "Common/MessageHandler";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { customPivotHeaderRenderer } from "Explorer/Quickstart/Shared/QuickstartRenderUtilities";
|
||||
import {
|
||||
loadDataCommand,
|
||||
newDbAndCollectionCommand,
|
||||
newDbAndCollectionCommandForDisplay,
|
||||
queriesCommand,
|
||||
queriesCommandForDisplay,
|
||||
} from "Explorer/Quickstart/VCoreMongoQuickstartCommands";
|
||||
import { useTerminal } from "hooks/useTerminal";
|
||||
import React, { useState } from "react";
|
||||
import { ReactTabKind, useTabs } from "../../hooks/useTabs";
|
||||
|
||||
enum GuideSteps {
|
||||
Login,
|
||||
NewTable,
|
||||
DistributeTable,
|
||||
LoadData,
|
||||
Query,
|
||||
}
|
||||
|
||||
export const VcoreMongoQuickstartGuide: React.FC = (): JSX.Element => {
|
||||
const [currentStep, setCurrentStep] = useState<number>(0);
|
||||
|
||||
const onCopyBtnClicked = (selector: string): void => {
|
||||
const textfield: HTMLInputElement = document.querySelector(selector);
|
||||
textfield.select();
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
return (
|
||||
<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="small">Getting started in Cosmos DB Mongo DB (vCore)</Text>
|
||||
{currentStep < 5 && (
|
||||
<Pivot
|
||||
style={{ marginTop: 10, width: "100%" }}
|
||||
selectedKey={GuideSteps[currentStep]}
|
||||
onLinkClick={(item?: PivotItem) => setCurrentStep(Object.values(GuideSteps).indexOf(item.props.itemKey))}
|
||||
>
|
||||
<PivotItem
|
||||
headerText="Connect"
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 0)
|
||||
}
|
||||
itemKey={GuideSteps[0]}
|
||||
onClick={() => {
|
||||
setCurrentStep(0);
|
||||
}}
|
||||
>
|
||||
<Stack style={{ marginTop: 20 }}>
|
||||
<Text>
|
||||
A hosted mongosh (mongo shell) is provided for this quick start. You are automatically logged in to
|
||||
mongosh, allowing you to interact with your database directly.
|
||||
<br />
|
||||
<br />
|
||||
When not in the quick start guide, connecting to Azure Cosmos DB for MongoDB vCore is straightforward
|
||||
using your connection string.
|
||||
<br />
|
||||
<br />
|
||||
<Link
|
||||
aria-label="View connection string"
|
||||
href=""
|
||||
onClick={() => sendMessage({ type: MessageTypes.OpenVCoreMongoConnectionStringsBlade })}
|
||||
>
|
||||
View connection string
|
||||
</Link>
|
||||
<br />
|
||||
<br />
|
||||
This string contains placeholders for <user> and <password>. Replace them with your chosen
|
||||
username and password to establish a secure connection to your cluster. Depending on your environment,
|
||||
you may need to adjust firewall rules or configure private endpoints in the ‘Networking’
|
||||
tab of your database settings, or modify your own network's firewall settings, to successfully
|
||||
connect.
|
||||
</Text>
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="New collection"
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 1)
|
||||
}
|
||||
itemKey={GuideSteps[1]}
|
||||
onClick={() => setCurrentStep(1)}
|
||||
>
|
||||
<Stack style={{ marginTop: 20 }}>
|
||||
<Text>
|
||||
In MongoDB, data is stored in collections, which are analogous to tables in relational databases.
|
||||
Collections contain documents, each of which consists of field and value pairs. The fields in
|
||||
documents are similar to the columns in a relational database table. One key advantage of MongoDB is
|
||||
that these documents within a collection can have different fields.
|
||||
<br />
|
||||
You're now going to create a new database and a collection within that database using the Mongo
|
||||
shell. In MongoDB, creating a database or a collection is implicit. This means that databases and
|
||||
collections are created when you first reference them in a command, so no explicit creation command is
|
||||
necessary.
|
||||
</Text>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 270 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(newDbAndCollectionCommand)}
|
||||
>
|
||||
Create new database and collection
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="newDbAndCollectionCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={newDbAndCollectionCommandForDisplay}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
backgroundColor: "#EEEEEE",
|
||||
fontFamily:
|
||||
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
iconProps={{
|
||||
iconName: "Copy",
|
||||
}}
|
||||
onClick={() => onCopyBtnClicked("#newDbAndCollectionCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Load data"
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 2)
|
||||
}
|
||||
itemKey={GuideSteps[2]}
|
||||
onClick={() => setCurrentStep(2)}
|
||||
>
|
||||
<Stack style={{ marginTop: 20 }}>
|
||||
<Text>
|
||||
Now that you've created your database and collection, it's time to populate your collection
|
||||
with data. In MongoDB, data is stored as documents, which are structured as field and value pairs.
|
||||
<br />
|
||||
<br />
|
||||
Let's populate your sampleCollection with data. We'll add 10 documents representing books,
|
||||
each with a title, author, and number of pages, to your sampleCollection in the quickstartDB database.
|
||||
</Text>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 200 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(loadDataCommand)}
|
||||
>
|
||||
Create distributed table
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="loadDataCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={loadDataCommand}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
backgroundColor: "#EEEEEE",
|
||||
fontFamily:
|
||||
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
iconProps={{
|
||||
iconName: "Copy",
|
||||
}}
|
||||
onClick={() => onCopyBtnClicked("#loadDataCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Queries"
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 3)
|
||||
}
|
||||
itemKey={GuideSteps[3]}
|
||||
onClick={() => setCurrentStep(3)}
|
||||
>
|
||||
<Stack style={{ marginTop: 20 }}>
|
||||
<Text>
|
||||
Once you’ve inserted data into your sampleCollection, you can retrieve it using queries. MongoDB
|
||||
queries can be as simple or as complex as you need them to be, allowing you to filter, sort, and limit
|
||||
results.
|
||||
</Text>
|
||||
<DefaultButton
|
||||
style={{ marginTop: 16, width: 110 }}
|
||||
onClick={() => useTerminal.getState().sendMessage(queriesCommand)}
|
||||
>
|
||||
Load data
|
||||
</DefaultButton>
|
||||
<Stack horizontal style={{ marginTop: 16 }}>
|
||||
<TextField
|
||||
id="queriesCommand"
|
||||
multiline
|
||||
rows={5}
|
||||
readOnly
|
||||
defaultValue={queriesCommandForDisplay}
|
||||
styles={{
|
||||
root: { width: "90%" },
|
||||
field: {
|
||||
backgroundColor: "#EEEEEE",
|
||||
fontFamily:
|
||||
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
iconProps={{
|
||||
iconName: "Copy",
|
||||
}}
|
||||
onClick={() => onCopyBtnClicked("#queriesCommand")}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="Integrations"
|
||||
onRenderItemLink={(props, defaultRenderer) =>
|
||||
customPivotHeaderRenderer(props, defaultRenderer, currentStep, 4)
|
||||
}
|
||||
itemKey={GuideSteps[4]}
|
||||
onClick={() => setCurrentStep(4)}
|
||||
>
|
||||
<Stack>
|
||||
<Text>
|
||||
Cosmos DB for MongoDB vCore seamlessly integrates with Azure services. These integrations enable
|
||||
Cosmos DB for MongoDB and its partner products to directly interoperate, ensuring a smooth and unified
|
||||
experience, that just works.
|
||||
</Text>
|
||||
<Stack horizontal>
|
||||
<Stack style={{ marginTop: 20, marginRight: 20 }}>
|
||||
<Text>
|
||||
<b>First party integrations</b>
|
||||
<br />
|
||||
<br />
|
||||
<b>Azure Monitor</b>
|
||||
<br />
|
||||
Azure monitor provides comprehensive monitoring and diagnostics for Cosmos DB for Mongo DB. Learn
|
||||
more
|
||||
<br />
|
||||
<br />
|
||||
<b>Azure Networking</b>
|
||||
<br />
|
||||
Azure Networking seamlessly integrates with Azure Cosmos DB for Mongo DB for fast and secure data
|
||||
access. Learn more
|
||||
<br />
|
||||
<br />
|
||||
<b>PowerShell/CLI/ARM</b>
|
||||
<br />
|
||||
PowerShell/CLI/ARM seamlessly integrates with Azure Cosmos DB for Mongo DB for efficient
|
||||
management and automation. Learn more
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack style={{ marginTop: 20, marginLeft: 20 }}>
|
||||
<Text>
|
||||
<b>Application platforms integrations</b>
|
||||
<br />
|
||||
<br />
|
||||
<b>Vercel</b>
|
||||
<br />
|
||||
Vercel is a cloud platform for hosting static front ends and serverless functions, with instant
|
||||
deployments, automated scaling, and Next.js integration. Learn more
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack horizontal style={{ padding: "16px 20px", boxShadow: "inset 0px 1px 0px rgba(204, 204, 204, 0.8)" }}>
|
||||
<DefaultButton disabled={currentStep === 0} onClick={() => setCurrentStep(currentStep - 1)}>
|
||||
Previous
|
||||
</DefaultButton>
|
||||
{currentStep < 4 && (
|
||||
<PrimaryButton onClick={() => setCurrentStep(currentStep + 1)} style={{ marginLeft: 8 }}>
|
||||
Next
|
||||
</PrimaryButton>
|
||||
)}
|
||||
{currentStep === 4 && (
|
||||
<PrimaryButton
|
||||
onClick={() => useTabs.getState().closeReactTab(ReactTabKind.Quickstart)}
|
||||
style={{ marginLeft: 8 }}
|
||||
>
|
||||
Done
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -260,30 +260,33 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
let title: string;
|
||||
let subtitle: string;
|
||||
|
||||
switch (userContext.apiType) {
|
||||
case "Postgres":
|
||||
title = "Welcome to Azure Cosmos DB for PostgreSQL";
|
||||
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
|
||||
break;
|
||||
case "VCoreMongo":
|
||||
title = "Welcome to Azure Cosmos DB for MongoDB (vCore)";
|
||||
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
|
||||
break;
|
||||
default:
|
||||
title = "Welcome to Azure Cosmos DB";
|
||||
subtitle = "Globally distributed, multi-model database service for any scale";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="connectExplorerContainer">
|
||||
<form className="connectExplorerFormContainer">
|
||||
<div className="splashScreenContainer">
|
||||
<div className="splashScreen">
|
||||
<h1
|
||||
className="title"
|
||||
role="heading"
|
||||
aria-label={
|
||||
userContext.apiType === "Postgres"
|
||||
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
||||
: "Welcome to Azure Cosmos DB"
|
||||
}
|
||||
>
|
||||
{userContext.apiType === "Postgres"
|
||||
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
||||
: "Welcome to Azure Cosmos DB"}
|
||||
<h1 className="title" role="heading" aria-label={title}>
|
||||
{title}
|
||||
<FeaturePanelLauncher />
|
||||
</h1>
|
||||
<div className="subtitle">
|
||||
{userContext.apiType === "Postgres"
|
||||
? "Get started with our sample datasets, documentation, and additional tools."
|
||||
: "Globally distributed, multi-model database service for any scale"}
|
||||
</div>
|
||||
<div className="subtitle">{subtitle}</div>
|
||||
{this.getSplashScreenButtons()}
|
||||
{useCarousel.getState().showCoachMark && (
|
||||
<Coachmark
|
||||
@@ -313,7 +316,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
</TeachingBubbleContent>
|
||||
</Coachmark>
|
||||
)}
|
||||
{userContext.apiType === "Postgres" ? (
|
||||
{userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo" ? (
|
||||
<Stack horizontal style={{ margin: "0 auto", width: "84%" }} tokens={{ childrenGap: 16 }}>
|
||||
<Stack style={{ width: "33%" }}>
|
||||
<Text
|
||||
@@ -380,7 +383,8 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
if (
|
||||
userContext.apiType === "SQL" ||
|
||||
userContext.apiType === "Mongo" ||
|
||||
(userContext.apiType === "Postgres" && !userContext.isReplica)
|
||||
(userContext.apiType === "Postgres" && !userContext.isReplica) ||
|
||||
userContext.apiType === "VCoreMongo"
|
||||
) {
|
||||
const launchQuickstartBtn = {
|
||||
id: "quickstartDescription",
|
||||
@@ -388,9 +392,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
title: "Launch quick start",
|
||||
description: "Launch a quick start tutorial to get started with sample data",
|
||||
onClick: () => {
|
||||
userContext.apiType === "Postgres"
|
||||
? useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart)
|
||||
: this.container.onNewCollectionClicked({ isQuickstart: true });
|
||||
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
|
||||
} else {
|
||||
this.container.onNewCollectionClicked({ isQuickstart: true });
|
||||
}
|
||||
traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType });
|
||||
},
|
||||
};
|
||||
@@ -405,39 +411,65 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
heroes.push(newNotebookBtn);
|
||||
}
|
||||
|
||||
heroes.push(this.getShellCard());
|
||||
heroes.push(this.getThirdCard());
|
||||
return heroes;
|
||||
}
|
||||
|
||||
private getShellCard() {
|
||||
if (userContext.apiType === "Postgres") {
|
||||
const postgreShellBtn = {
|
||||
return {
|
||||
iconSrc: PowerShellIcon,
|
||||
title: "PostgreSQL Shell",
|
||||
description: "Create table and interact with data using PostgreSQL’s shell interface",
|
||||
onClick: () => this.container.openNotebookTerminal(TerminalKind.Postgres),
|
||||
};
|
||||
heroes.push(postgreShellBtn);
|
||||
} else {
|
||||
const newContainerBtn = {
|
||||
iconSrc: ContainersIcon,
|
||||
title: `New ${getCollectionName()}`,
|
||||
description: "Create a new container for storage and throughput",
|
||||
onClick: () => {
|
||||
this.container.onNewCollectionClicked();
|
||||
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
|
||||
},
|
||||
};
|
||||
heroes.push(newContainerBtn);
|
||||
}
|
||||
|
||||
const connectBtn = {
|
||||
iconSrc: ConnectIcon,
|
||||
title: userContext.apiType === "Postgres" ? "Connect with pgAdmin" : "Connect",
|
||||
description:
|
||||
userContext.apiType === "Postgres"
|
||||
? "Prefer pgAdmin? Find your connection strings here"
|
||||
: "Prefer using your own choice of tooling? Find the connection string you need to connect",
|
||||
onClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect),
|
||||
};
|
||||
heroes.push(connectBtn);
|
||||
if (userContext.apiType === "VCoreMongo") {
|
||||
return {
|
||||
iconSrc: PowerShellIcon,
|
||||
title: "Mongo Shell",
|
||||
description: "Create a collection and interact with data using MongoDB's shell interface",
|
||||
onClick: () => this.container.openNotebookTerminal(TerminalKind.VCoreMongo),
|
||||
};
|
||||
}
|
||||
|
||||
return heroes;
|
||||
return {
|
||||
iconSrc: ContainersIcon,
|
||||
title: `New ${getCollectionName()}`,
|
||||
description: "Create a new container for storage and throughput",
|
||||
onClick: () => {
|
||||
this.container.onNewCollectionClicked();
|
||||
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private getThirdCard() {
|
||||
let icon = ConnectIcon;
|
||||
let title = "Connect";
|
||||
let description = "Prefer using your own choice of tooling? Find the connection string you need to connect";
|
||||
let onClick = () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect);
|
||||
|
||||
if (userContext.apiType === "Postgres") {
|
||||
title = "Connect with pgAdmin";
|
||||
description = "Prefer pgAdmin? Find your connection strings here";
|
||||
}
|
||||
|
||||
if (userContext.apiType === "VCoreMongo") {
|
||||
icon = ContainersIcon;
|
||||
title = "Connect with Studio 3T";
|
||||
description = "Prefer Studio 3T? Find your connection strings here";
|
||||
onClick = () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect);
|
||||
}
|
||||
|
||||
return {
|
||||
iconSrc: icon,
|
||||
title: title,
|
||||
description: description,
|
||||
onClick: onClick,
|
||||
};
|
||||
}
|
||||
|
||||
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
|
||||
@@ -587,6 +619,8 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
},
|
||||
];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<Stack>
|
||||
@@ -724,6 +758,8 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
cdbLiveTv,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<Stack>
|
||||
@@ -749,24 +785,46 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
);
|
||||
}
|
||||
|
||||
private postgresNextStepItems: { link: string; title: string; description: string }[] = [
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2208312",
|
||||
title: "Data Modeling",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: " https://go.microsoft.com/fwlink/?linkid=2206941 ",
|
||||
title: "How to choose a Distribution Column",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2207425",
|
||||
title: "Build Apps with Python/Java/Django",
|
||||
description: "",
|
||||
},
|
||||
];
|
||||
|
||||
private vcoreMongoNextStepItems: { link: string; title: string; description: string }[] = [
|
||||
{
|
||||
link:
|
||||
"https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/how-to-migrate-native-tools?tabs=export-import",
|
||||
title: "Migrate Data",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search-ai",
|
||||
title: "Build AI apps with Vector Search",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link:
|
||||
"https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/tutorial-nodejs-web-app?tabs=github-codespaces",
|
||||
title: "Build Apps with Nodejs",
|
||||
description: "",
|
||||
},
|
||||
];
|
||||
|
||||
private getNextStepItems(): JSX.Element {
|
||||
const items: { link: string; title: string; description: string }[] = [
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2208312",
|
||||
title: "Data Modeling",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: " https://go.microsoft.com/fwlink/?linkid=2206941 ",
|
||||
title: "How to choose a Distribution Column",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2207425",
|
||||
title: "Build Apps with Python/Java/Django",
|
||||
description: "",
|
||||
},
|
||||
];
|
||||
const items = userContext.apiType === "Postgres" ? this.postgresNextStepItems : this.vcoreMongoNextStepItems;
|
||||
|
||||
return (
|
||||
<Stack style={{ minWidth: 124, maxWidth: 296 }}>
|
||||
@@ -785,24 +843,44 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
);
|
||||
}
|
||||
|
||||
private postgresLearnMoreItems: { link: string; title: string; description: string }[] = [
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2207226",
|
||||
title: "Performance Tuning",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2208037",
|
||||
title: "Useful Diagnostic Queries",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2205270",
|
||||
title: "Distributed SQL Reference",
|
||||
description: "",
|
||||
},
|
||||
];
|
||||
|
||||
private vcoreMongoLearnMoreItems: { link: string; title: string; description: string }[] = [
|
||||
{
|
||||
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search",
|
||||
title: "Vector Search",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/how-to-create-text-index",
|
||||
title: "Text Indexing",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/troubleshoot-common-issues",
|
||||
title: "Troubleshoot common issues",
|
||||
description: "",
|
||||
},
|
||||
];
|
||||
|
||||
private getTipsAndLearnMoreItems(): JSX.Element {
|
||||
const items: { link: string; title: string; description: string }[] = [
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2207226",
|
||||
title: "Performance Tuning",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2208037",
|
||||
title: "Useful Diagnostic Queries",
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
link: "https://go.microsoft.com/fwlink/?linkid=2205270",
|
||||
title: "Distributed SQL Reference",
|
||||
description: "",
|
||||
},
|
||||
];
|
||||
const items = userContext.apiType === "Postgres" ? this.postgresLearnMoreItems : this.vcoreMongoLearnMoreItems;
|
||||
|
||||
return (
|
||||
<Stack style={{ minWidth: 124, maxWidth: 296 }}>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
|
||||
import { PoolIdType } from "Common/Constants";
|
||||
import { configContext } from "ConfigContext";
|
||||
import { NotebookWorkspaceConnectionInfo, PostgresFirewallRule } from "Contracts/DataModels";
|
||||
import { NotebookWorkspaceConnectionInfo } from "Contracts/DataModels";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { NotebookTerminalComponent } from "Explorer/Controls/Notebook/NotebookTerminalComponent";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||
import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification";
|
||||
import { QuickstartGuide } from "Explorer/Quickstart/QuickstartGuide";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
|
||||
import { userContext } from "UserContext";
|
||||
import { armRequest } from "Utils/arm/request";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import FirewallRuleScreenshot from "../../../images/firewallRule.png";
|
||||
|
||||
interface QuickstartTabProps {
|
||||
explorer: Explorer;
|
||||
@@ -26,29 +26,12 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
|
||||
forwardingId: notebookServerInfo.forwardingId,
|
||||
});
|
||||
|
||||
const checkFirewallRules = async (): Promise<void> => {
|
||||
const firewallRulesUri = `${userContext.databaseAccount.id}/firewallRules`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const response: any = await armRequest({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path: firewallRulesUri,
|
||||
method: "GET",
|
||||
apiVersion: "2022-11-08",
|
||||
});
|
||||
const firewallRules: PostgresFirewallRule[] = response?.data?.value || response?.value || [];
|
||||
const isEnabled = firewallRules.some(
|
||||
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"
|
||||
);
|
||||
setIsAllPublicIPAddressEnabled(isEnabled);
|
||||
|
||||
// If the firewall rule is not added, check every 30 seconds to see if the user has added the rule
|
||||
if (!isEnabled && useTabs.getState().activeReactTab === ReactTabKind.Quickstart) {
|
||||
setTimeout(checkFirewallRules, 30000);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkFirewallRules();
|
||||
checkFirewallRules(
|
||||
"2022-11-08",
|
||||
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255",
|
||||
setIsAllPublicIPAddressEnabled
|
||||
);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -61,7 +44,13 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
|
||||
<QuickstartGuide />
|
||||
</Stack>
|
||||
<Stack style={{ width: "50%", borderLeft: "black solid 1px" }}>
|
||||
{!isAllPublicIPAddressEnabled && <QuickstartFirewallNotification />}
|
||||
{!isAllPublicIPAddressEnabled && (
|
||||
<QuickstartFirewallNotification
|
||||
messageType={MessageTypes.OpenPostgresNetworkingBlade}
|
||||
screenshot={FirewallRuleScreenshot}
|
||||
shellName="PostgreSQL"
|
||||
/>
|
||||
)}
|
||||
{isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && (
|
||||
<NotebookTerminalComponent
|
||||
notebookServerInfo={getNotebookServerInfo()}
|
||||
|
||||
44
src/Explorer/Tabs/Shared/CheckFirewallRules.ts
Normal file
44
src/Explorer/Tabs/Shared/CheckFirewallRules.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { configContext } from "ConfigContext";
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { userContext } from "UserContext";
|
||||
import { armRequest } from "Utils/arm/request";
|
||||
|
||||
export async function checkFirewallRules(
|
||||
apiVersion: string,
|
||||
firewallRulesPredicate: (rule: DataModels.FirewallRule) => unknown,
|
||||
isAllPublicIPAddressesEnabled?: ko.Observable<boolean> | React.Dispatch<React.SetStateAction<boolean>>,
|
||||
setMessageFunc?: (message: string) => void,
|
||||
message?: string
|
||||
): Promise<void> {
|
||||
const firewallRulesUri = `${userContext.databaseAccount.id}/firewallRules`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const response: any = await armRequest({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path: firewallRulesUri,
|
||||
method: "GET",
|
||||
apiVersion: apiVersion,
|
||||
});
|
||||
const firewallRules: DataModels.FirewallRule[] = response?.data?.value || response?.value || [];
|
||||
const isEnabled = firewallRules.some(firewallRulesPredicate);
|
||||
|
||||
if (isAllPublicIPAddressesEnabled) {
|
||||
isAllPublicIPAddressesEnabled(isEnabled);
|
||||
}
|
||||
|
||||
if (setMessageFunc) {
|
||||
if (!isEnabled) {
|
||||
setMessageFunc(message);
|
||||
} else {
|
||||
setMessageFunc(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
// If the firewall rule is not added, check every 30 seconds to see if the user has added the rule
|
||||
if (!isEnabled) {
|
||||
setTimeout(
|
||||
() =>
|
||||
checkFirewallRules(apiVersion, firewallRulesPredicate, isAllPublicIPAddressesEnabled, setMessageFunc, message),
|
||||
30000
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
|
||||
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
||||
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
|
||||
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
|
||||
import { VcoreMongoConnectTab } from "Explorer/Tabs/VCoreMongoConnectTab";
|
||||
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
|
||||
import { userContext } from "UserContext";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||
@@ -35,7 +37,16 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||
<MessageBar
|
||||
messageBarType={MessageBarType.warning}
|
||||
actions={
|
||||
<MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenPostgresNetworkingBlade })}>
|
||||
<MessageBarButton
|
||||
onClick={() =>
|
||||
sendMessage({
|
||||
type:
|
||||
userContext.apiType === "VCoreMongo"
|
||||
? MessageTypes.OpenVCoreMongoNetworkingBlade
|
||||
: MessageTypes.OpenPostgresNetworkingBlade,
|
||||
})
|
||||
}
|
||||
>
|
||||
Change network settings
|
||||
</MessageBarButton>
|
||||
}
|
||||
@@ -252,11 +263,21 @@ const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => {
|
||||
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
|
||||
switch (activeReactTab) {
|
||||
case ReactTabKind.Connect:
|
||||
return userContext.apiType === "Postgres" ? <PostgresConnectTab /> : <ConnectTab />;
|
||||
return userContext.apiType === "VCoreMongo" ? (
|
||||
<VcoreMongoConnectTab />
|
||||
) : userContext.apiType === "Postgres" ? (
|
||||
<PostgresConnectTab />
|
||||
) : (
|
||||
<ConnectTab />
|
||||
);
|
||||
case ReactTabKind.Home:
|
||||
return <SplashScreen explorer={explorer} />;
|
||||
case ReactTabKind.Quickstart:
|
||||
return <QuickstartTab explorer={explorer} />;
|
||||
return userContext.apiType === "VCoreMongo" ? (
|
||||
<VcoreMongoQuickstartTab explorer={explorer} />
|
||||
) : (
|
||||
<QuickstartTab explorer={explorer} />
|
||||
);
|
||||
case ReactTabKind.QueryCopilot:
|
||||
return <QueryCopilotTab explorer={explorer} />;
|
||||
default:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Spinner, SpinnerSize } from "@fluentui/react";
|
||||
import { configContext } from "ConfigContext";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification";
|
||||
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
|
||||
import * as ko from "knockout";
|
||||
import * as React from "react";
|
||||
import { armRequest } from "Utils/arm/request";
|
||||
import FirewallRuleScreenshot from "../../../images/firewallRule.png";
|
||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
@@ -18,6 +19,7 @@ export interface TerminalTabOptions extends ViewModels.TabOptions {
|
||||
account: DataModels.DatabaseAccount;
|
||||
container: Explorer;
|
||||
kind: ViewModels.TerminalKind;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,12 +32,19 @@ class NotebookTerminalComponentAdapter implements ReactAdapter {
|
||||
private getNotebookServerInfo: () => DataModels.NotebookWorkspaceConnectionInfo,
|
||||
private getDatabaseAccount: () => DataModels.DatabaseAccount,
|
||||
private getTabId: () => string,
|
||||
private getUsername: () => string,
|
||||
private isAllPublicIPAddressesEnabled: ko.Observable<boolean>
|
||||
) {}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
if (!this.isAllPublicIPAddressesEnabled()) {
|
||||
return <QuickstartFirewallNotification />;
|
||||
return (
|
||||
<QuickstartFirewallNotification
|
||||
messageType={MessageTypes.OpenPostgresNetworkingBlade}
|
||||
screenshot={FirewallRuleScreenshot}
|
||||
shellName="PostgreSQL"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.parameters() ? (
|
||||
@@ -43,6 +52,7 @@ class NotebookTerminalComponentAdapter implements ReactAdapter {
|
||||
notebookServerInfo={this.getNotebookServerInfo()}
|
||||
databaseAccount={this.getDatabaseAccount()}
|
||||
tabId={this.getTabId()}
|
||||
username={this.getUsername()}
|
||||
/>
|
||||
) : (
|
||||
<Spinner styles={{ root: { marginTop: 10 } }} size={SpinnerSize.large}></Spinner>
|
||||
@@ -64,6 +74,7 @@ export default class TerminalTab extends TabsBase {
|
||||
() => this.getNotebookServerInfo(options),
|
||||
() => userContext?.databaseAccount,
|
||||
() => this.tabId,
|
||||
() => this.getUsername(),
|
||||
this.isAllPublicIPAddressesEnabled
|
||||
);
|
||||
this.notebookTerminalComponentAdapter.parameters = ko.computed<boolean>(() => {
|
||||
@@ -79,7 +90,21 @@ export default class TerminalTab extends TabsBase {
|
||||
});
|
||||
|
||||
if (options.kind === ViewModels.TerminalKind.Postgres) {
|
||||
this.checkPostgresFirewallRules();
|
||||
checkFirewallRules(
|
||||
"2022-11-08",
|
||||
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255",
|
||||
this.isAllPublicIPAddressesEnabled
|
||||
);
|
||||
}
|
||||
|
||||
if (options.kind === ViewModels.TerminalKind.VCoreMongo) {
|
||||
checkFirewallRules(
|
||||
"2023-03-01-preview",
|
||||
(rule) =>
|
||||
rule.name.startsWith("AllowAllAzureServicesAndResourcesWithinAzureIps") ||
|
||||
(rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"),
|
||||
this.isAllPublicIPAddressesEnabled
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +140,10 @@ export default class TerminalTab extends TabsBase {
|
||||
endpointSuffix = "postgresql";
|
||||
break;
|
||||
|
||||
case ViewModels.TerminalKind.VCoreMongo:
|
||||
endpointSuffix = "mongovcore";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Terminal kind: ${options.kind} not supported`);
|
||||
}
|
||||
@@ -127,24 +156,11 @@ export default class TerminalTab extends TabsBase {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkPostgresFirewallRules(): Promise<void> {
|
||||
const firewallRulesUri = `${userContext.databaseAccount.id}/firewallRules`;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const response: any = await armRequest({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path: firewallRulesUri,
|
||||
method: "GET",
|
||||
apiVersion: "2022-11-08",
|
||||
});
|
||||
const firewallRules: DataModels.PostgresFirewallRule[] = response?.data?.value || response?.value || [];
|
||||
const isEnabled = firewallRules.some(
|
||||
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"
|
||||
);
|
||||
this.isAllPublicIPAddressesEnabled(isEnabled);
|
||||
|
||||
// If the firewall rule is not added, check every 30 seconds to see if the user has added the rule
|
||||
if (!isEnabled) {
|
||||
setTimeout(() => this.checkPostgresFirewallRules(), 30000);
|
||||
private getUsername(): string {
|
||||
if (userContext.apiType !== "VCoreMongo" || !userContext?.vcoreMongoConnectionParams?.adminLogin) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return userContext.vcoreMongoConnectionParams.adminLogin;
|
||||
}
|
||||
}
|
||||
|
||||
37
src/Explorer/Tabs/VCoreMongoConnectTab.tsx
Normal file
37
src/Explorer/Tabs/VCoreMongoConnectTab.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { IconButton, ITextFieldStyles, Stack, TextField } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import { userContext } from "UserContext";
|
||||
|
||||
export const VcoreMongoConnectTab: React.FC = (): JSX.Element => {
|
||||
const { adminLogin, connectionString } = userContext.vcoreMongoConnectionParams;
|
||||
const displayConnectionString = connectionString.replace("<user>", adminLogin);
|
||||
|
||||
const textfieldStyles: Partial<ITextFieldStyles> = {
|
||||
root: { width: "100%" },
|
||||
field: { backgroundColor: "rgb(230, 230, 230)" },
|
||||
fieldGroup: { borderColor: "rgb(138, 136, 134)" },
|
||||
subComponentStyles: { label: { fontWeight: 400 } },
|
||||
description: { fontWeight: 400 },
|
||||
};
|
||||
|
||||
const onCopyBtnClicked = (selector: string): void => {
|
||||
const textfield: HTMLInputElement = document.querySelector(selector);
|
||||
textfield.select();
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", padding: 16 }}>
|
||||
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||
<TextField
|
||||
label="MongoDB SRV connection URL"
|
||||
id="mongoSrvConnectionURL"
|
||||
readOnly
|
||||
value={displayConnectionString}
|
||||
styles={textfieldStyles}
|
||||
/>
|
||||
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#mongoSrvConnectionURL")} />
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
80
src/Explorer/Tabs/VCoreMongoQuickstartTab.tsx
Normal file
80
src/Explorer/Tabs/VCoreMongoQuickstartTab.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
|
||||
import { PoolIdType } from "Common/Constants";
|
||||
import { NotebookWorkspaceConnectionInfo } from "Contracts/DataModels";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { NotebookTerminalComponent } from "Explorer/Controls/Notebook/NotebookTerminalComponent";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||
import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification";
|
||||
import { VcoreMongoQuickstartGuide } from "Explorer/Quickstart/VCoreMongoQuickstartGuide";
|
||||
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
|
||||
import { userContext } from "UserContext";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import FirewallRuleScreenshot from "../../../images/vcoreMongoFirewallRule.png";
|
||||
|
||||
interface VCoreMongoQuickstartTabProps {
|
||||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export const VcoreMongoQuickstartTab: React.FC<VCoreMongoQuickstartTabProps> = ({
|
||||
explorer,
|
||||
}: VCoreMongoQuickstartTabProps): JSX.Element => {
|
||||
const notebookServerInfo = useNotebook((state) => state.notebookServerInfo);
|
||||
const [isAllPublicIPAddressEnabled, setIsAllPublicIPAddressEnabled] = useState<boolean>(true);
|
||||
|
||||
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
|
||||
authToken: notebookServerInfo.authToken,
|
||||
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/mongovcore`,
|
||||
forwardingId: notebookServerInfo.forwardingId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
checkFirewallRules(
|
||||
"2023-03-01-preview",
|
||||
(rule) =>
|
||||
rule.name.startsWith("AllowAllAzureServicesAndResourcesWithinAzureIps") ||
|
||||
(rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"),
|
||||
setIsAllPublicIPAddressEnabled
|
||||
);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
explorer.allocateContainer(PoolIdType.DefaultPoolId);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack style={{ width: "100%" }} horizontal>
|
||||
<Stack style={{ width: "50%" }}>
|
||||
<VcoreMongoQuickstartGuide />
|
||||
</Stack>
|
||||
<Stack style={{ width: "50%", borderLeft: "black solid 1px" }}>
|
||||
{!isAllPublicIPAddressEnabled && (
|
||||
<QuickstartFirewallNotification
|
||||
messageType={MessageTypes.OpenVCoreMongoNetworkingBlade}
|
||||
screenshot={FirewallRuleScreenshot}
|
||||
shellName="MongoDB"
|
||||
/>
|
||||
)}
|
||||
{isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && (
|
||||
<NotebookTerminalComponent
|
||||
notebookServerInfo={getNotebookServerInfo()}
|
||||
databaseAccount={userContext.databaseAccount}
|
||||
tabId="QuickstartVcoreMongoShell"
|
||||
username={userContext.vcoreMongoConnectionParams.adminLogin}
|
||||
/>
|
||||
)}
|
||||
{isAllPublicIPAddressEnabled && !notebookServerInfo?.notebookServerEndpoint && (
|
||||
<Stack style={{ margin: "auto 0" }}>
|
||||
<Text block style={{ margin: "auto" }}>
|
||||
Connecting to the Mongo shell.
|
||||
</Text>
|
||||
<Text block style={{ margin: "auto" }}>
|
||||
If the cluster was just created, this could take up to a minute.
|
||||
</Text>
|
||||
<Spinner styles={{ root: { marginTop: 16 } }} size={SpinnerSize.large}></Spinner>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user