mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 15:06:55 +00:00
Implement connection string tab for postgres (#1334)
This commit is contained in:
parent
7c77ffda6c
commit
81dfd76198
@ -397,6 +397,8 @@ export interface DataExplorerInputsFrame {
|
|||||||
dataExplorerVersion?: string;
|
dataExplorerVersion?: string;
|
||||||
defaultCollectionThroughput?: CollectionCreationDefaults;
|
defaultCollectionThroughput?: CollectionCreationDefaults;
|
||||||
isPostgresAccount?: boolean;
|
isPostgresAccount?: boolean;
|
||||||
|
// TODO: Update this param in the OSS extension to remove isFreeTier, isMarlinServerGroup, and make nodes a flat array instead of an nested array
|
||||||
|
connectionStringParams?: any;
|
||||||
flights?: readonly string[];
|
flights?: readonly string[];
|
||||||
features?: {
|
features?: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
|
133
src/Explorer/Tabs/PostgresConnectTab.tsx
Normal file
133
src/Explorer/Tabs/PostgresConnectTab.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
Dropdown,
|
||||||
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
IDropdownOption,
|
||||||
|
ITextFieldStyles,
|
||||||
|
Label,
|
||||||
|
Link,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
TooltipHost,
|
||||||
|
} from "@fluentui/react";
|
||||||
|
import React from "react";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
|
||||||
|
export const PostgresConnectTab: React.FC = (): JSX.Element => {
|
||||||
|
const { adminLogin, nodes, enablePublicIpAccess } = userContext.postgresConnectionStrParams;
|
||||||
|
const [usePgBouncerPort, setUsePgBouncerPort] = React.useState<boolean>(false);
|
||||||
|
const [selectedNode, setSelectedNode] = React.useState<string>(nodes?.[0]?.value);
|
||||||
|
const portNumber = usePgBouncerPort ? "6432" : "5432";
|
||||||
|
|
||||||
|
const onCopyBtnClicked = (selector: string): void => {
|
||||||
|
const textfield: HTMLInputElement = document.querySelector(selector);
|
||||||
|
textfield.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
};
|
||||||
|
|
||||||
|
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 nodesDropdownOptions: IDropdownOption[] = nodes.map((node) => ({
|
||||||
|
key: node.value,
|
||||||
|
text: node.text,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const postgresSQLConnectionURL = `postgres://${adminLogin}:{your_password}@${selectedNode}:${portNumber}/citus?sslmode=require`;
|
||||||
|
const psql = `psql "host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require"`;
|
||||||
|
const jdbc = `jdbc:postgresql://${selectedNode}:${portNumber}/citus?user=${adminLogin}&password={your_password}&sslmode=require`;
|
||||||
|
const libpq = `host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require`;
|
||||||
|
const adoDotNet = `Server=${selectedNode};Database=citus;Port=${portNumber};User Id=${adminLogin};Password={your_password};Ssl Mode=Require;`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: "100%", padding: 16 }}>
|
||||||
|
<Stack horizontal verticalAlign="center" style={{ marginBottom: 8 }}>
|
||||||
|
<Label style={{ marginRight: 8 }}>Public IP addresses on worker nodes:</Label>
|
||||||
|
<TooltipHost
|
||||||
|
content="
|
||||||
|
You can enable or disable public IP addresses on the worker nodes on 'Networking' page of your server group."
|
||||||
|
>
|
||||||
|
<Icon style={{ margin: "5px 8px 0 0", cursor: "default" }} iconName="Info" />
|
||||||
|
</TooltipHost>
|
||||||
|
|
||||||
|
<TextField value={enablePublicIpAccess ? "On" : "Off"} readOnly disabled />
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal style={{ marginBottom: 8 }}>
|
||||||
|
<Label style={{ marginRight: 85 }}>Show connection strings for</Label>
|
||||||
|
<Dropdown
|
||||||
|
options={nodesDropdownOptions}
|
||||||
|
selectedKey={selectedNode}
|
||||||
|
onChange={(_, option) => {
|
||||||
|
const selectedNode = option.key as string;
|
||||||
|
setSelectedNode(selectedNode);
|
||||||
|
if (!selectedNode.startsWith("c.")) {
|
||||||
|
setUsePgBouncerPort(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ width: 200 }}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal style={{ marginBottom: 8 }}>
|
||||||
|
<Label style={{ marginRight: 44 }}>PgBouncer connection strings</Label>
|
||||||
|
<Checkbox
|
||||||
|
boxSide="end"
|
||||||
|
checked={usePgBouncerPort}
|
||||||
|
onChange={(_, checked: boolean) => setUsePgBouncerPort(checked)}
|
||||||
|
disabled={!selectedNode?.startsWith("c.")}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="PostgreSQL connection URL"
|
||||||
|
id="postgresSQLConnectionURL"
|
||||||
|
readOnly
|
||||||
|
value={postgresSQLConnectionURL}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#postgresSQLConnectionURL")} />
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField label="psql" id="psql" readOnly value={psql} styles={textfieldStyles} />
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#psql")} />
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField label="JDBC" id="JDBC" readOnly value={jdbc} styles={textfieldStyles} />
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#JDBC")} />
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
label="Node.js, Python, Ruby, PHP, C++ (libpq)"
|
||||||
|
id="libpq"
|
||||||
|
readOnly
|
||||||
|
value={libpq}
|
||||||
|
styles={textfieldStyles}
|
||||||
|
/>
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#libpq")} />
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
||||||
|
<TextField label="ADO.NET" id="adoDotNet" readOnly value={adoDotNet} styles={textfieldStyles} />
|
||||||
|
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#adoDotNet")} />
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Label>Secure connections</Label>
|
||||||
|
<Text>
|
||||||
|
Only secure connections are supported. For production use cases, we recommend using the 'verify-full'
|
||||||
|
mode to enforce TLS certificate verification. You will need to download the Hyperscale (Citus) certificate, and
|
||||||
|
provide it when connecting to the database.{" "}
|
||||||
|
<Link href="https://go.microsoft.com/fwlink/?linkid=2155061" target="_blank">
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -2,10 +2,12 @@ import { CollectionTabKind } from "Contracts/ViewModels";
|
|||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
|
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
|
||||||
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
||||||
|
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
|
||||||
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
|
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
|
||||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
|
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
|
||||||
import errorIcon from "../../../images/close-black.svg";
|
import errorIcon from "../../../images/close-black.svg";
|
||||||
import { useObservable } from "../../hooks/useObservable";
|
import { useObservable } from "../../hooks/useObservable";
|
||||||
@ -189,7 +191,7 @@ const onKeyPressReactTab = (e: KeyboardEvent, tabKind: ReactTabKind): void => {
|
|||||||
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
|
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
|
||||||
switch (activeReactTab) {
|
switch (activeReactTab) {
|
||||||
case ReactTabKind.Connect:
|
case ReactTabKind.Connect:
|
||||||
return <ConnectTab />;
|
return userContext.apiType === "Postgres" ? <PostgresConnectTab /> : <ConnectTab />;
|
||||||
case ReactTabKind.Home:
|
case ReactTabKind.Home:
|
||||||
return <SplashScreen explorer={explorer} />;
|
return <SplashScreen explorer={explorer} />;
|
||||||
case ReactTabKind.Quickstart:
|
case ReactTabKind.Quickstart:
|
||||||
|
@ -26,6 +26,20 @@ export interface CollectionCreationDefaults {
|
|||||||
throughput: ThroughputDefaults;
|
throughput: ThroughputDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Node {
|
||||||
|
text: string;
|
||||||
|
value: string;
|
||||||
|
ariaLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostgresConnectionStrParams {
|
||||||
|
adminLogin: string;
|
||||||
|
enablePublicIpAccess: boolean;
|
||||||
|
nodes: Node[];
|
||||||
|
isMarlinServerGroup: boolean;
|
||||||
|
isFreeTier: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface UserContext {
|
interface UserContext {
|
||||||
readonly authType?: AuthType;
|
readonly authType?: AuthType;
|
||||||
readonly masterKey?: string;
|
readonly masterKey?: string;
|
||||||
@ -52,6 +66,7 @@ interface UserContext {
|
|||||||
collectionId: string;
|
collectionId: string;
|
||||||
partitionKey?: string;
|
partitionKey?: string;
|
||||||
};
|
};
|
||||||
|
readonly postgresConnectionStrParams?: PostgresConnectionStrParams;
|
||||||
collectionCreationDefaults: CollectionCreationDefaults;
|
collectionCreationDefaults: CollectionCreationDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
} from "../Platform/Hosted/HostedUtils";
|
} from "../Platform/Hosted/HostedUtils";
|
||||||
import { CollectionCreation } from "../Shared/Constants";
|
import { CollectionCreation } from "../Shared/Constants";
|
||||||
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
|
||||||
import { PortalEnv, updateUserContext, userContext } from "../UserContext";
|
import { Node, PortalEnv, updateUserContext, userContext } from "../UserContext";
|
||||||
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
||||||
import { getMsalInstance } from "../Utils/AuthorizationUtils";
|
import { getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||||
@ -357,6 +357,20 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
|
|
||||||
if (inputs.isPostgresAccount) {
|
if (inputs.isPostgresAccount) {
|
||||||
updateUserContext({ apiType: "Postgres" });
|
updateUserContext({ apiType: "Postgres" });
|
||||||
|
|
||||||
|
if (inputs.connectionStringParams) {
|
||||||
|
// TODO: Remove after the nodes param has been updated to be a flat array in the OSS extension
|
||||||
|
let flattenedNodesArray: Node[] = [];
|
||||||
|
inputs.connectionStringParams.nodes?.forEach((node: Node | Node[]) => {
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
flattenedNodesArray = [...flattenedNodesArray, ...node];
|
||||||
|
} else {
|
||||||
|
flattenedNodesArray.push(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inputs.connectionStringParams.nodes = flattenedNodesArray;
|
||||||
|
updateUserContext({ postgresConnectionStrParams: inputs.connectionStringParams });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputs.features) {
|
if (inputs.features) {
|
||||||
|
Loading…
Reference in New Issue
Block a user