mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-24 19:31:36 +00:00
Compare commits
5 Commits
cloudshell
...
documentdb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98d7e89712 | ||
|
|
d45e10f5ac | ||
|
|
cfb5db4df6 | ||
|
|
922ca5c523 | ||
|
|
bafe002fa3 |
@@ -44,6 +44,12 @@ export interface DatabaseAccountExtendedProperties {
|
|||||||
publicNetworkAccess?: string;
|
publicNetworkAccess?: string;
|
||||||
enablePriorityBasedExecution?: boolean;
|
enablePriorityBasedExecution?: boolean;
|
||||||
vcoreMongoEndpoint?: string;
|
vcoreMongoEndpoint?: string;
|
||||||
|
apiProperties?: ApiProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiProperties {
|
||||||
|
/* Describes the version of the MongoDB account. */
|
||||||
|
serverVersion?: "3.2" | "3.6" | "4.0" | "4.2" | "5.0" | "6.0" | "7.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseAccountResponseLocation {
|
export interface DatabaseAccountResponseLocation {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
|||||||
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
||||||
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
|
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
|
||||||
|
import { getVSCodeUrl } from "Utils/VSCodeExtensionUtils";
|
||||||
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
@@ -289,40 +290,8 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a VS Code DocumentDB connection URL using the current user's MongoDB connection parameters.
|
|
||||||
* Double-encodes the updated connection string for safe usage in VS Code URLs.
|
|
||||||
*
|
|
||||||
* The DocumentDB VS Code extension requires double encoding for connection strings.
|
|
||||||
* See: https://microsoft.github.io/vscode-documentdb/manual/how-to-construct-url.html#double-encoding
|
|
||||||
*
|
|
||||||
* @returns {string} The encoded VS Code DocumentDB connection URL.
|
|
||||||
*/
|
|
||||||
private getDocumentDbUrl() {
|
|
||||||
const { adminLogin: adminLoginuserName = "", connectionString = "" } = userContext.vcoreMongoConnectionParams;
|
|
||||||
const updatedConnectionString = connectionString.replace(/<(user|username)>:<password>/i, adminLoginuserName);
|
|
||||||
const encodedUpdatedConnectionString = encodeURIComponent(encodeURIComponent(updatedConnectionString));
|
|
||||||
const documentDbUrl = `vscode://ms-azuretools.vscode-documentdb?connectionString=${encodedUpdatedConnectionString}`;
|
|
||||||
return documentDbUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCosmosDbUrl() {
|
|
||||||
const activeTab = useTabs.getState().activeTab;
|
|
||||||
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
|
|
||||||
const database = encodeURIComponent(activeTab?.collection?.databaseId);
|
|
||||||
const container = encodeURIComponent(activeTab?.collection?.id());
|
|
||||||
const baseUrl = `vscode://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
|
|
||||||
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
|
|
||||||
return vscodeUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getVSCodeUrl(): string {
|
|
||||||
const isvCore = (userContext.apiType || userContext.databaseAccount.kind) === "VCoreMongo";
|
|
||||||
return isvCore ? this.getDocumentDbUrl() : this.getCosmosDbUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public openInVsCode(): void {
|
public openInVsCode(): void {
|
||||||
const vscodeUrl = this.getVSCodeUrl();
|
const vscodeUrl = getVSCodeUrl();
|
||||||
const openVSCodeDialogProps: DialogProps = {
|
const openVSCodeDialogProps: DialogProps = {
|
||||||
linkProps: {
|
linkProps: {
|
||||||
linkText: "Download Visual Studio Code",
|
linkText: "Download Visual Studio Code",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Accordion top class
|
* Accordion top class
|
||||||
*/
|
*/
|
||||||
import { makeStyles, tokens } from "@fluentui/react-components";
|
import { Link, makeStyles, tokens } from "@fluentui/react-components";
|
||||||
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
|
import { DocumentAddRegular, LinkMultipleRegular, OpenRegular } from "@fluentui/react-icons";
|
||||||
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
||||||
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||||
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
|
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
|
||||||
@@ -119,7 +119,7 @@ const FabricHomeScreenButton: React.FC<FabricHomeScreenButtonProps & { className
|
|||||||
}) => {
|
}) => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
return (
|
return (
|
||||||
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick}>
|
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick} tabIndex={0}>
|
||||||
<div className={styles.buttonUpperPart}>{icon}</div>
|
<div className={styles.buttonUpperPart}>{icon}</div>
|
||||||
<div aria-label={title} className={styles.buttonLowerPart}>
|
<div aria-label={title} className={styles.buttonLowerPart}>
|
||||||
<div>{title}</div>
|
<div>{title}</div>
|
||||||
@@ -147,7 +147,7 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
|
|||||||
{
|
{
|
||||||
title: "Sample data",
|
title: "Sample data",
|
||||||
description: "Automatically load sample data in your database",
|
description: "Automatically load sample data in your database",
|
||||||
icon: <img src={CosmosDbBlackIcon} />,
|
icon: <img src={CosmosDbBlackIcon} alt={"Azure Cosmos DB icon"} aria-hidden="true" />,
|
||||||
onClick: () => setOpenSampleDataImportDialog(true),
|
onClick: () => setOpenSampleDataImportDialog(true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -181,16 +181,18 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
|
|||||||
explorer={props.explorer}
|
explorer={props.explorer}
|
||||||
databaseName={userContext.fabricContext?.databaseName}
|
databaseName={userContext.fabricContext?.databaseName}
|
||||||
/>
|
/>
|
||||||
<div className={styles.title} role="heading" aria-label={title}>
|
<div className={styles.title} role="heading" aria-label={title} aria-level={1}>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
{getSplashScreenButtons()}
|
{getSplashScreenButtons()}
|
||||||
{/* <div className={styles.footer}>
|
{
|
||||||
Need help?{" "}
|
<div className={styles.footer}>
|
||||||
<Link href="https://aka.ms/cosmosdbfabricdocs" target="_blank">
|
Need help?{" "}
|
||||||
Learn more <img src={LinkIcon} alt="Learn more" />
|
<Link href="https://learn.microsoft.com/fabric/database/cosmos-db/overview" target="_blank">
|
||||||
</Link>
|
Learn more <OpenRegular />
|
||||||
</div> */}
|
</Link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</CosmosFluentProvider>
|
</CosmosFluentProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ export const DISABLE_HISTORY = `set +o history`;
|
|||||||
* Used when shell initialization or connection fails.
|
* Used when shell initialization or connection fails.
|
||||||
*/
|
*/
|
||||||
export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close this tab and initiate a new shell session if needed.\\033[0m\\n" && disown -a && exit`;
|
export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close this tab and initiate a new shell session if needed.\\033[0m\\n" && disown -a && exit`;
|
||||||
|
/**
|
||||||
|
* Command that displays error message with MongoDB networking guidance and exits the shell session.
|
||||||
|
* Used when MongoDB shell connection fails due to networking issues.
|
||||||
|
*/
|
||||||
|
export const EXIT_COMMAND_MONGO = ` printf "\\033[1;31mSession ended. Please close this tab and initiate a new shell session if needed.\\033[0m\\n" && printf "\\033[1;36mPlease use the 'Add Azure Cloud Shell IPs' button in the Networking blade to allow Cloud Shell access, if not already configured.\\033[0m\\n" && disown -a && exit`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This command runs mongosh in no-database and quiet mode,
|
* This command runs mongosh in no-database and quiet mode,
|
||||||
@@ -40,6 +45,14 @@ export abstract class AbstractShellHandler {
|
|||||||
abstract getTerminalSuppressedData(): string[];
|
abstract getTerminalSuppressedData(): string[];
|
||||||
updateTerminalData?(data: string): string;
|
updateTerminalData?(data: string): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the exit command to use when connection fails.
|
||||||
|
* Can be overridden by subclasses to provide custom exit commands.
|
||||||
|
*/
|
||||||
|
protected getExitCommand(): string {
|
||||||
|
return EXIT_COMMAND;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the complete initialization command sequence for the shell.
|
* Constructs the complete initialization command sequence for the shell.
|
||||||
*
|
*
|
||||||
@@ -64,7 +77,7 @@ export abstract class AbstractShellHandler {
|
|||||||
START_MARKER,
|
START_MARKER,
|
||||||
DISABLE_HISTORY,
|
DISABLE_HISTORY,
|
||||||
...setupCommands,
|
...setupCommands,
|
||||||
`{ ${connectionCommand}; } || true;${EXIT_COMMAND}`,
|
`{ ${connectionCommand}; } || true;${this.getExitCommand()}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
return allCommands.join("\n").concat("\n");
|
return allCommands.join("\n").concat("\n");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { userContext } from "../../../../UserContext";
|
import { userContext } from "../../../../UserContext";
|
||||||
import { filterAndCleanTerminalOutput, getHostFromUrl, getMongoShellRemoveInfoText } from "../Utils/CommonUtils";
|
import { filterAndCleanTerminalOutput, getHostFromUrl, getMongoShellRemoveInfoText } from "../Utils/CommonUtils";
|
||||||
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND } from "./AbstractShellHandler";
|
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND, EXIT_COMMAND_MONGO } from "./AbstractShellHandler";
|
||||||
|
|
||||||
export class MongoShellHandler extends AbstractShellHandler {
|
export class MongoShellHandler extends AbstractShellHandler {
|
||||||
private _key: string;
|
private _key: string;
|
||||||
@@ -48,6 +48,10 @@ export class MongoShellHandler extends AbstractShellHandler {
|
|||||||
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
|
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getExitCommand(): string {
|
||||||
|
return EXIT_COMMAND_MONGO;
|
||||||
|
}
|
||||||
|
|
||||||
updateTerminalData(data: string): string {
|
updateTerminalData(data: string): string {
|
||||||
return filterAndCleanTerminalOutput(data, this._removeInfoText);
|
return filterAndCleanTerminalOutput(data, this._removeInfoText);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { userContext } from "../../../../UserContext";
|
import { userContext } from "../../../../UserContext";
|
||||||
import { filterAndCleanTerminalOutput, getMongoShellRemoveInfoText } from "../Utils/CommonUtils";
|
import { filterAndCleanTerminalOutput, getMongoShellRemoveInfoText } from "../Utils/CommonUtils";
|
||||||
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND } from "./AbstractShellHandler";
|
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND, EXIT_COMMAND_MONGO } from "./AbstractShellHandler";
|
||||||
|
|
||||||
export class VCoreMongoShellHandler extends AbstractShellHandler {
|
export class VCoreMongoShellHandler extends AbstractShellHandler {
|
||||||
private _endpoint: string | undefined;
|
private _endpoint: string | undefined;
|
||||||
@@ -35,6 +35,13 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
|
|||||||
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
|
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override getExitCommand to include MongoDB networking guidance
|
||||||
|
*/
|
||||||
|
protected getExitCommand(): string {
|
||||||
|
return EXIT_COMMAND_MONGO;
|
||||||
|
}
|
||||||
|
|
||||||
updateTerminalData(data: string): string {
|
updateTerminalData(data: string): string {
|
||||||
return filterAndCleanTerminalOutput(data, this._removeInfoText);
|
return filterAndCleanTerminalOutput(data, this._removeInfoText);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const validCloudShellRegions = new Set([
|
|||||||
"westeurope",
|
"westeurope",
|
||||||
"centralindia",
|
"centralindia",
|
||||||
"southeastasia",
|
"southeastasia",
|
||||||
"westcentralus",
|
|
||||||
"usgovvirginia",
|
"usgovvirginia",
|
||||||
"usgovarizona",
|
"usgovarizona",
|
||||||
]);
|
]);
|
||||||
@@ -41,7 +40,6 @@ export const getNormalizedRegion = (region: string, defaultCloudshellRegion: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
const regionMap: Record<string, string> = {
|
const regionMap: Record<string, string> = {
|
||||||
centralus: "westcentralus",
|
|
||||||
eastus2: "eastus",
|
eastus2: "eastus",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFi
|
|||||||
import { getShellNameForDisplay } from "Explorer/Tabs/CloudShellTab/Utils/CommonUtils";
|
import { getShellNameForDisplay } from "Explorer/Tabs/CloudShellTab/Utils/CommonUtils";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import FirewallRuleScreenshot from "../../../../images/firewallRule.png";
|
import FirewallRuleScreenshot from "../../../../images/firewallRule.png";
|
||||||
import VcoreFirewallRuleScreenshot from "../../../../images/vcoreMongoFirewallRule.png";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
@@ -25,15 +24,15 @@ export abstract class BaseTerminalComponentAdapter implements ReactAdapter {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
public renderComponent(): JSX.Element {
|
||||||
|
if (this.kind === ViewModels.TerminalKind.Mongo || this.kind === ViewModels.TerminalKind.VCoreMongo) {
|
||||||
|
return this.renderTerminalComponent();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isAllPublicIPAddressesEnabled()) {
|
if (!this.isAllPublicIPAddressesEnabled()) {
|
||||||
return (
|
return (
|
||||||
<QuickstartFirewallNotification
|
<QuickstartFirewallNotification
|
||||||
messageType={this.getMessageType()}
|
messageType={this.getMessageType()}
|
||||||
screenshot={
|
screenshot={FirewallRuleScreenshot}
|
||||||
this.kind === ViewModels.TerminalKind.Mongo || this.kind === ViewModels.TerminalKind.VCoreMongo
|
|
||||||
? VcoreFirewallRuleScreenshot
|
|
||||||
: FirewallRuleScreenshot
|
|
||||||
}
|
|
||||||
shellName={getShellNameForDisplay(this.kind)}
|
shellName={getShellNameForDisplay(this.kind)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
55
src/Utils/VSCodeExtensionUtils.ts
Normal file
55
src/Utils/VSCodeExtensionUtils.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { useTabs } from "hooks/useTabs";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
import { getHostFromUrl } from "./../Explorer/Tabs/CloudShellTab/Utils/CommonUtils";
|
||||||
|
|
||||||
|
export const DOCUMENTDB_VSCODE_EXTENSION_BASEURL = "vscode://ms-azuretools.vscode-documentdb";
|
||||||
|
export const COSMOSDB_VSCODE_EXTENSION_BASEURL = "vscode://ms-azuretools.vscode-cosmosdb";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a VS Code DocumentDB connection URL using the current user's MongoDB connection parameters.
|
||||||
|
* Double-encodes the updated connection string for safe usage in VS Code URLs.
|
||||||
|
*
|
||||||
|
* The DocumentDB VS Code extension requires double encoding for connection strings.
|
||||||
|
* See: https://microsoft.github.io/vscode-documentdb/manual/how-to-construct-url.html#double-encoding
|
||||||
|
*
|
||||||
|
* @returns {string} The encoded VS Code DocumentDB connection URL.
|
||||||
|
*/
|
||||||
|
export const getVSCodeUrl = (): string => {
|
||||||
|
const isvCore = (userContext.apiType || userContext.databaseAccount.kind) === "VCoreMongo";
|
||||||
|
const isMongo =
|
||||||
|
userContext.apiType === "Mongo" && userContext.databaseAccount?.properties?.apiProperties?.serverVersion !== "3.2";
|
||||||
|
return isvCore ? getDocumentDbUrl() : isMongo ? getMongoRUUrl() : getCosmosDbUrl();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCosmosDbUrl = () => {
|
||||||
|
const activeTab = useTabs.getState().activeTab;
|
||||||
|
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
|
||||||
|
const database = encodeURIComponent(activeTab?.collection?.databaseId);
|
||||||
|
const container = encodeURIComponent(activeTab?.collection?.id());
|
||||||
|
const baseUrl = `${COSMOSDB_VSCODE_EXTENSION_BASEURL}?resourceId=${resourceId}`;
|
||||||
|
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
|
||||||
|
return vscodeUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMongoRUUrl = () => {
|
||||||
|
const activeTab = useTabs.getState().activeTab;
|
||||||
|
const databaseAccount = userContext.databaseAccount;
|
||||||
|
const host = getHostFromUrl(databaseAccount.properties?.mongoEndpoint);
|
||||||
|
const port = 10255;
|
||||||
|
const database = activeTab?.collection?.databaseId;
|
||||||
|
const container = activeTab?.collection?.id();
|
||||||
|
const encodedUpdatedConnectionString = encodeURIComponent(`mongodb://${databaseAccount?.name}@${host}:${port}`);
|
||||||
|
const documentDbUrl = `${DOCUMENTDB_VSCODE_EXTENSION_BASEURL}?connectionString=${encodedUpdatedConnectionString}${
|
||||||
|
database ? `&database=${database}` : ""
|
||||||
|
}${container ? `&collection=${container}` : ""}`;
|
||||||
|
|
||||||
|
return documentDbUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDocumentDbUrl = () => {
|
||||||
|
const { adminLogin: adminLoginuserName = "", connectionString = "" } = userContext.vcoreMongoConnectionParams;
|
||||||
|
const updatedConnectionString = connectionString.replace(/<(user|username)>:<password>/i, adminLoginuserName);
|
||||||
|
const encodedUpdatedConnectionString = encodeURIComponent(encodeURIComponent(updatedConnectionString));
|
||||||
|
const documentDbUrl = `${DOCUMENTDB_VSCODE_EXTENSION_BASEURL}?connectionString=${encodedUpdatedConnectionString}`;
|
||||||
|
return documentDbUrl;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user