mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-02-21 18:03:44 +00:00
Compare commits
3 Commits
fix_duplic
...
users/saks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4eee1d745b | ||
|
|
09d266028f | ||
|
|
2778bcec82 |
@@ -6,7 +6,7 @@ import { MinimalQueryIterator, nextPage } from "../IteratorUtilities";
|
||||
|
||||
// Redact sensitive information from BadRequest errors with specific codes
|
||||
export const redactSyntaxErrorMessage = (error: unknown): unknown => {
|
||||
const codesToRedact = ["SC1001", "SC2001", "SC1010"];
|
||||
const codesToRedact = ["SC1001", "SC2001"];
|
||||
|
||||
try {
|
||||
// Handle error objects with a message property
|
||||
|
||||
@@ -543,6 +543,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
<span style={{ float: "left", transform: "translateX(-50%)" }}>
|
||||
{this.props.instantMaximumThroughput.toLocaleString(ThroughputInputAutoPilotV3Component.LOCALE_EN_US)}
|
||||
</span>
|
||||
<span style={{ float: "right" }}>
|
||||
{this.props.softAllowedMaximumThroughput.toLocaleString(ThroughputInputAutoPilotV3Component.LOCALE_EN_US)}
|
||||
</span>
|
||||
<span style={{ float: "right" }} data-test="soft-allowed-maximum-throughput">
|
||||
{this.props.softAllowedMaximumThroughput.toLocaleString(ThroughputInputAutoPilotV3Component.LOCALE_EN_US)}
|
||||
</span>
|
||||
|
||||
@@ -426,6 +426,15 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
>
|
||||
5,000
|
||||
</span>
|
||||
<span
|
||||
style={
|
||||
{
|
||||
"float": "right",
|
||||
}
|
||||
}
|
||||
>
|
||||
1,000,000
|
||||
</span>
|
||||
<span
|
||||
data-test="soft-allowed-maximum-throughput"
|
||||
style={
|
||||
@@ -1025,6 +1034,15 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
||||
>
|
||||
5,000
|
||||
</span>
|
||||
<span
|
||||
style={
|
||||
{
|
||||
"float": "right",
|
||||
}
|
||||
}
|
||||
>
|
||||
1,000,000
|
||||
</span>
|
||||
<span
|
||||
data-test="soft-allowed-maximum-throughput"
|
||||
style={
|
||||
@@ -1602,6 +1620,15 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
||||
>
|
||||
5,000
|
||||
</span>
|
||||
<span
|
||||
style={
|
||||
{
|
||||
"float": "right",
|
||||
}
|
||||
}
|
||||
>
|
||||
1,000,000
|
||||
</span>
|
||||
<span
|
||||
data-test="soft-allowed-maximum-throughput"
|
||||
style={
|
||||
|
||||
@@ -17,12 +17,18 @@ import SynapseIcon from "../../../../images/synapse-link.svg";
|
||||
import VSCodeIcon from "../../../../images/vscode.svg";
|
||||
import { AuthType } from "../../../AuthType";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { Platform, configContext } from "../../../ConfigContext";
|
||||
import { configContext, Platform } from "../../../ConfigContext";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import {
|
||||
isVCoreMongoNativeAuthDisabled,
|
||||
userContext,
|
||||
VCoreMongoNativeAuthDisabledMessage,
|
||||
VCoreMongoNativeAuthLearnMoreUrl,
|
||||
} from "../../../UserContext";
|
||||
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import { useDialog } from "../../Controls/Dialog";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useNotebook } from "../../Notebook/useNotebook";
|
||||
import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||
@@ -508,12 +514,21 @@ function createOpenTerminalButtonByKind(
|
||||
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 isNativeAuthDisabled = terminalKind === ViewModels.TerminalKind.VCoreMongo && isVCoreMongoNativeAuthDisabled();
|
||||
const disableButton =
|
||||
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||
(!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled) ||
|
||||
isNativeAuthDisabled;
|
||||
return {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
if (isNativeAuthDisabled) {
|
||||
useDialog.getState().showOkModalDialog("Native Authentication Disabled", VCoreMongoNativeAuthDisabledMessage, {
|
||||
linkText: "Learn more",
|
||||
linkUrl: VCoreMongoNativeAuthLearnMoreUrl,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (useNotebook.getState().isNotebookEnabled || userContext.features.enableCloudShell) {
|
||||
container.openNotebookTerminal(terminalKind);
|
||||
}
|
||||
@@ -522,7 +537,7 @@ function createOpenTerminalButtonByKind(
|
||||
hasPopup: false,
|
||||
disabled: disableButton,
|
||||
ariaLabel: label,
|
||||
tooltipText: !disableButton ? "" : tooltip,
|
||||
tooltipText: isNativeAuthDisabled ? VCoreMongoNativeAuthDisabledMessage : !disableButton ? "" : tooltip,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,14 @@ import VisualStudioIcon from "../../../images/VisualStudio.svg";
|
||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
isVCoreMongoNativeAuthDisabled,
|
||||
userContext,
|
||||
VCoreMongoNativeAuthDisabledMessage,
|
||||
VCoreMongoNativeAuthLearnMoreUrl,
|
||||
} from "../../UserContext";
|
||||
import { getCollectionName } from "../../Utils/APITypeUtils";
|
||||
import { useDialog } from "../Controls/Dialog";
|
||||
import Explorer from "../Explorer";
|
||||
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
|
||||
import { useNotebook } from "../Notebook/useNotebook";
|
||||
@@ -423,11 +429,23 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
|
||||
}
|
||||
|
||||
if (userContext.apiType === "VCoreMongo") {
|
||||
const isNativeAuthDisabled = isVCoreMongoNativeAuthDisabled();
|
||||
return {
|
||||
iconSrc: PowerShellIcon,
|
||||
title: "Mongo Shell",
|
||||
description: "Create a collection and interact with data using MongoDB's shell interface",
|
||||
onClick: () => container.openNotebookTerminal(TerminalKind.VCoreMongo),
|
||||
onClick: () => {
|
||||
if (isNativeAuthDisabled) {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog("Native Authentication Disabled", VCoreMongoNativeAuthDisabledMessage, {
|
||||
linkText: "Learn more",
|
||||
linkUrl: VCoreMongoNativeAuthLearnMoreUrl,
|
||||
});
|
||||
} else {
|
||||
container.openNotebookTerminal(TerminalKind.VCoreMongo);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
|
||||
import { Link, MessageBar, MessageBarType, Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
|
||||
import { PoolIdType } from "Common/Constants";
|
||||
import { NotebookWorkspaceConnectionInfo } from "Contracts/DataModels";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
@@ -8,7 +8,12 @@ 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 {
|
||||
isVCoreMongoNativeAuthDisabled,
|
||||
userContext,
|
||||
VCoreMongoNativeAuthDisabledMessage,
|
||||
VCoreMongoNativeAuthLearnMoreUrl,
|
||||
} from "UserContext";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import FirewallRuleScreenshot from "../../../images/vcoreMongoFirewallRule.png";
|
||||
|
||||
@@ -21,6 +26,7 @@ export const VcoreMongoQuickstartTab: React.FC<VCoreMongoQuickstartTabProps> = (
|
||||
}: VCoreMongoQuickstartTabProps): JSX.Element => {
|
||||
const notebookServerInfo = useNotebook((state) => state.notebookServerInfo);
|
||||
const [isAllPublicIPAddressEnabled, setIsAllPublicIPAddressEnabled] = useState<boolean>(true);
|
||||
const isNativeAuthDisabled = isVCoreMongoNativeAuthDisabled();
|
||||
|
||||
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
|
||||
authToken: notebookServerInfo.authToken,
|
||||
@@ -48,14 +54,26 @@ export const VcoreMongoQuickstartTab: React.FC<VCoreMongoQuickstartTabProps> = (
|
||||
<VcoreMongoQuickstartGuide />
|
||||
</Stack>
|
||||
<Stack style={{ width: "50%", borderLeft: "black solid 1px" }}>
|
||||
{!isAllPublicIPAddressEnabled && (
|
||||
{isNativeAuthDisabled && (
|
||||
<Stack style={{ margin: "auto", padding: 20 }}>
|
||||
<MessageBar messageBarType={MessageBarType.warning} isMultiline={true}>
|
||||
<Text>
|
||||
{VCoreMongoNativeAuthDisabledMessage}{" "}
|
||||
<Link href={VCoreMongoNativeAuthLearnMoreUrl} target="_blank">
|
||||
Learn more
|
||||
</Link>
|
||||
</Text>
|
||||
</MessageBar>
|
||||
</Stack>
|
||||
)}
|
||||
{!isNativeAuthDisabled && !isAllPublicIPAddressEnabled && (
|
||||
<QuickstartFirewallNotification
|
||||
messageType={MessageTypes.OpenVCoreMongoNetworkingBlade}
|
||||
screenshot={FirewallRuleScreenshot}
|
||||
shellName="MongoDB"
|
||||
/>
|
||||
)}
|
||||
{isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && (
|
||||
{!isNativeAuthDisabled && isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && (
|
||||
<NotebookTerminalComponent
|
||||
notebookServerInfo={getNotebookServerInfo()}
|
||||
databaseAccount={userContext.databaseAccount}
|
||||
@@ -63,7 +81,7 @@ export const VcoreMongoQuickstartTab: React.FC<VCoreMongoQuickstartTabProps> = (
|
||||
username={userContext.vcoreMongoConnectionParams.adminLogin}
|
||||
/>
|
||||
)}
|
||||
{isAllPublicIPAddressEnabled && !notebookServerInfo?.notebookServerEndpoint && (
|
||||
{!isNativeAuthDisabled && isAllPublicIPAddressEnabled && !notebookServerInfo?.notebookServerEndpoint && (
|
||||
<Stack style={{ margin: "auto 0" }}>
|
||||
<Text block style={{ margin: "auto" }}>
|
||||
Connecting to the Mongo shell.
|
||||
|
||||
@@ -120,7 +120,7 @@ const App = (): JSX.Element => {
|
||||
}, [explorer]);
|
||||
|
||||
// Track interactive phase for both ContainerCopyPanel and DivExplorer paths
|
||||
useInteractive(MetricScenario.ApplicationLoad, !!config);
|
||||
useInteractive(MetricScenario.ApplicationLoad);
|
||||
|
||||
if (!explorer) {
|
||||
return <LoadingExplorer />;
|
||||
|
||||
@@ -56,13 +56,6 @@ class ScenarioMonitor {
|
||||
});
|
||||
}
|
||||
|
||||
private devLog(msg: string) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[Metrics] ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
start(scenario: MetricScenario) {
|
||||
if (this.contexts.has(scenario)) {
|
||||
return;
|
||||
@@ -93,10 +86,6 @@ class ScenarioMonitor {
|
||||
ctx.phases.set(phase, { startMarkName: phaseStartMarkName });
|
||||
});
|
||||
|
||||
this.devLog(
|
||||
`scenario_start: ${scenario} | phases=${config.requiredPhases.join(", ")} | timeout=${config.timeoutMs}ms`,
|
||||
);
|
||||
|
||||
traceMark(Action.MetricsScenario, {
|
||||
event: "scenario_start",
|
||||
scenario,
|
||||
@@ -147,12 +136,6 @@ class ScenarioMonitor {
|
||||
const endTimeISO = endEntry ? new Date(navigationStart + endEntry.startTime).toISOString() : undefined;
|
||||
const durationMs = startEntry && endEntry ? endEntry.startTime - startEntry.startTime : undefined;
|
||||
|
||||
this.devLog(
|
||||
`phase_complete: ${scenario}.${phase} | ${
|
||||
durationMs !== null && durationMs !== undefined ? `${Math.round(durationMs)}ms` : "?"
|
||||
} | ${ctx.completed.size}/${ctx.config.requiredPhases.length} phases`,
|
||||
);
|
||||
|
||||
traceSuccess(Action.MetricsScenario, {
|
||||
event: "phase_complete",
|
||||
scenario,
|
||||
@@ -172,13 +155,6 @@ class ScenarioMonitor {
|
||||
return;
|
||||
}
|
||||
|
||||
// If an expected failure was flagged (auth, firewall, etc.), treat as success.
|
||||
if (ctx.hasExpectedFailure) {
|
||||
this.devLog(`phase_fail: ${scenario}.${phase} — expected failure, completing as healthy`);
|
||||
this.completePhase(scenario, phase);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the explicitly failed phase
|
||||
performance.mark(`scenario_${scenario}_${phase}_failed`);
|
||||
ctx.failed.add(phase);
|
||||
@@ -193,12 +169,6 @@ class ScenarioMonitor {
|
||||
// Build a snapshot with failure info
|
||||
const failureSnapshot = this.buildSnapshot(ctx, { final: false, timedOut: false });
|
||||
|
||||
this.devLog(
|
||||
`phase_fail: ${scenario}.${phase} | failed=[${Array.from(ctx.failed).join(", ")}] | completed=[${Array.from(
|
||||
ctx.completed,
|
||||
).join(", ")}]`,
|
||||
);
|
||||
|
||||
traceFailure(Action.MetricsScenario, {
|
||||
event: "phase_fail",
|
||||
scenario,
|
||||
@@ -207,7 +177,7 @@ class ScenarioMonitor {
|
||||
completedPhases: Array.from(ctx.completed).join(","),
|
||||
});
|
||||
|
||||
// Emit unhealthy immediately for unexpected failures
|
||||
// Emit unhealthy immediately
|
||||
this.emit(ctx, false, false, failureSnapshot);
|
||||
}
|
||||
|
||||
@@ -300,19 +270,6 @@ class ScenarioMonitor {
|
||||
ttfb: finalSnapshot.vitals?.ttfb,
|
||||
});
|
||||
|
||||
this.devLog(
|
||||
`scenario_end: ${ctx.scenario} | ${healthy ? "healthy" : "unhealthy"} | ${
|
||||
timedOut ? "timed out" : `${Math.round(finalSnapshot.durationMs)}ms`
|
||||
} | ${JSON.stringify({
|
||||
completedPhases: finalSnapshot.completed.join(", "),
|
||||
failedPhases: finalSnapshot.failedPhases?.join(", ") || "none",
|
||||
platform,
|
||||
api,
|
||||
phaseTimings: finalSnapshot.phaseTimings,
|
||||
vitals: finalSnapshot.vitals,
|
||||
})}`,
|
||||
);
|
||||
|
||||
// Call portal backend health metrics endpoint
|
||||
// If healthy is true (either completed successfully or timeout with expected failure), report healthy
|
||||
if (healthy) {
|
||||
|
||||
@@ -7,18 +7,14 @@ import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
|
||||
* Hook to automatically complete the Interactive phase when the component becomes interactive.
|
||||
* Uses requestAnimationFrame to complete after the browser has painted.
|
||||
*/
|
||||
export function useInteractive(scenario: MetricScenario, enabled = true) {
|
||||
export function useInteractive(scenario: MetricScenario) {
|
||||
const { completePhase } = useMetricScenario();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!enabled) {
|
||||
return undefined;
|
||||
}
|
||||
const id = requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
completePhase(scenario, CommonMetricPhase.Interactive);
|
||||
});
|
||||
return () => cancelAnimationFrame(id);
|
||||
}, [scenario, completePhase, enabled]);
|
||||
}, [scenario, completePhase]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,10 +42,25 @@ export interface PostgresConnectionStrParams {
|
||||
isMarlinServerGroup: boolean;
|
||||
isFreeTier: boolean;
|
||||
}
|
||||
export type VCoreMongoAuthMode = "NativeAuth" | "MicrosoftEntraID";
|
||||
|
||||
export interface VCoreMongoAuthConfig {
|
||||
allowedModes?: VCoreMongoAuthMode[];
|
||||
}
|
||||
|
||||
export interface VCoreMongoConnectionParams {
|
||||
adminLogin: string;
|
||||
connectionString: string;
|
||||
authConfig?: VCoreMongoAuthConfig;
|
||||
}
|
||||
|
||||
export const VCoreMongoNativeAuthLearnMoreUrl = "https://go.microsoft.com/fwlink/?linkid=2340100";
|
||||
|
||||
export const VCoreMongoNativeAuthDisabledMessage =
|
||||
"Native DocumentDB authentication is disabled on this cluster. You can use MongoDB Shell with Entra ID authentication outside of the Azure portal.";
|
||||
export function isVCoreMongoNativeAuthDisabled(): boolean {
|
||||
const allowedModes = userContext.vcoreMongoConnectionParams?.authConfig?.allowedModes || [];
|
||||
return allowedModes.length > 0 && !allowedModes.includes("NativeAuth");
|
||||
}
|
||||
|
||||
export interface FabricArtifactInfo {
|
||||
|
||||
Reference in New Issue
Block a user