@@ -21,11 +37,14 @@ export const CollapsedResourceTree: FunctionComponent
-
+
-
+
{userContext.apiType} API
diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts
index 6e13f07b5..bb8a2787b 100644
--- a/src/Common/Constants.ts
+++ b/src/Common/Constants.ts
@@ -340,7 +340,6 @@ export enum ConflictOperationType {
export enum ConnectionStatusType {
Connecting = "Connecting",
- Allocating = "Allocating",
Connected = "Connected",
Failed = "Connection Failed",
}
diff --git a/src/Common/MongoProxyClient.test.ts b/src/Common/MongoProxyClient.test.ts
index 3a5a02365..1c49141a0 100644
--- a/src/Common/MongoProxyClient.test.ts
+++ b/src/Common/MongoProxyClient.test.ts
@@ -3,8 +3,16 @@ import { resetConfigContext, updateConfigContext } from "../ConfigContext";
import { DatabaseAccount } from "../Contracts/DataModels";
import { Collection } from "../Contracts/ViewModels";
import DocumentId from "../Explorer/Tree/DocumentId";
+import { extractFeatures } from "../Platform/Hosted/extractFeatures";
import { updateUserContext } from "../UserContext";
-import { deleteDocument, getEndpoint, queryDocuments, readDocument, updateDocument } from "./MongoProxyClient";
+import {
+ deleteDocument,
+ getEndpoint,
+ getFeatureEndpointOrDefault,
+ queryDocuments,
+ readDocument,
+ updateDocument,
+} from "./MongoProxyClient";
const databaseId = "testDB";
@@ -246,4 +254,31 @@ describe("MongoProxyClient", () => {
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer");
});
});
+ describe("getFeatureEndpointOrDefault", () => {
+ beforeEach(() => {
+ resetConfigContext();
+ updateConfigContext({
+ BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
+ });
+ const params = new URLSearchParams({
+ "feature.mongoProxyEndpoint": "https://localhost:12901",
+ "feature.mongoProxyAPIs": "readDocument|createDocument",
+ });
+ const features = extractFeatures(params);
+ updateUserContext({
+ authType: AuthType.AAD,
+ features: features,
+ });
+ });
+
+ it("returns a local endpoint", () => {
+ const endpoint = getFeatureEndpointOrDefault("readDocument");
+ expect(endpoint).toEqual("https://localhost:12901/api/mongo/explorer");
+ });
+
+ it("returns a production endpoint", () => {
+ const endpoint = getFeatureEndpointOrDefault("deleteDocument");
+ expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/mongo/explorer");
+ });
+ });
});
diff --git a/src/Common/MongoProxyClient.ts b/src/Common/MongoProxyClient.ts
index 2945f1288..668a0ab16 100644
--- a/src/Common/MongoProxyClient.ts
+++ b/src/Common/MongoProxyClient.ts
@@ -6,6 +6,7 @@ import * as DataModels from "../Contracts/DataModels";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { Collection } from "../Contracts/ViewModels";
import DocumentId from "../Explorer/Tree/DocumentId";
+import { hasFlag } from "../Platform/Hosted/extractFeatures";
import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
@@ -78,7 +79,7 @@ export function queryDocuments(
: "",
};
- const endpoint = getEndpoint() || "";
+ const endpoint = getFeatureEndpointOrDefault("resourcelist") || "";
const headers = {
...defaultHeaders,
@@ -141,7 +142,8 @@ export function readDocument(
: "",
};
- const endpoint = getEndpoint();
+ const endpoint = getFeatureEndpointOrDefault("readDocument");
+
return window
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
method: "GET",
@@ -181,7 +183,7 @@ export function createDocument(
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
};
- const endpoint = getEndpoint();
+ const endpoint = getFeatureEndpointOrDefault("createDocument");
return window
.fetch(`${endpoint}/resourcelist?${queryString.stringify(params)}`, {
@@ -225,7 +227,7 @@ export function updateDocument(
? documentId.partitionKeyProperty
: "",
};
- const endpoint = getEndpoint();
+ const endpoint = getFeatureEndpointOrDefault("updateDocument");
return window
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
@@ -266,7 +268,7 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
? documentId.partitionKeyProperty
: "",
};
- const endpoint = getEndpoint();
+ const endpoint = getFeatureEndpointOrDefault("deleteDocument");
return window
.fetch(`${endpoint}?${queryString.stringify(params)}`, {
@@ -309,7 +311,7 @@ export function createMongoCollectionWithProxy(
autoPilotThroughput: params.autoPilotMaxThroughput?.toString(),
};
- const endpoint = getEndpoint();
+ const endpoint = getFeatureEndpointOrDefault("createCollectionWithProxy");
return window
.fetch(
@@ -333,8 +335,15 @@ export function createMongoCollectionWithProxy(
});
}
-export function getEndpoint(): string {
- let url = (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT) + "/api/mongo/explorer";
+export function getFeatureEndpointOrDefault(feature: string): string {
+ return hasFlag(userContext.features.mongoProxyAPIs, feature)
+ ? getEndpoint(userContext.features.mongoProxyEndpoint)
+ : getEndpoint();
+}
+
+export function getEndpoint(customEndpoint?: string): string {
+ let url = customEndpoint ? customEndpoint : configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
+ url += "/api/mongo/explorer";
if (userContext.authType === AuthType.EncryptedToken) {
url = url.replace("api/mongo", "api/guest/mongo");
diff --git a/src/Common/ResourceTreeContainer.tsx b/src/Common/ResourceTreeContainer.tsx
index 18a769b12..00693a2c0 100644
--- a/src/Common/ResourceTreeContainer.tsx
+++ b/src/Common/ResourceTreeContainer.tsx
@@ -1,4 +1,4 @@
-import React, { FunctionComponent } from "react";
+import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import refreshImg from "../../images/refresh-cosmos.svg";
import { AuthType } from "../AuthType";
@@ -6,6 +6,7 @@ import Explorer from "../Explorer/Explorer";
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
import { ResourceTree } from "../Explorer/Tree/ResourceTree";
import { userContext } from "../UserContext";
+import { NormalizedEventKey } from "./Constants";
export interface ResourceTreeContainerProps {
toggleLeftPaneExpanded: () => void;
@@ -18,6 +19,22 @@ export const ResourceTreeContainer: FunctionComponent {
+ const focusButton = useRef() as MutableRefObject;
+
+ useEffect(() => {
+ if (isLeftPaneExpanded) {
+ if (focusButton.current) {
+ focusButton.current.focus();
+ }
+ }
+ });
+
+ const onKeyPressToggleLeftPaneExpanded = (event: React.KeyboardEvent) => {
+ if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
+ toggleLeftPaneExpanded();
+ event.stopPropagation();
+ }
+ };
return (
{/* Collections Window - - Start */}
@@ -43,9 +60,11 @@ export const ResourceTreeContainer: FunctionComponent
diff --git a/src/Common/Tooltip/InfoTooltip.tsx b/src/Common/Tooltip/InfoTooltip.tsx
index 480aa9020..3ce33ca93 100644
--- a/src/Common/Tooltip/InfoTooltip.tsx
+++ b/src/Common/Tooltip/InfoTooltip.tsx
@@ -9,7 +9,7 @@ export const InfoTooltip: React.FunctionComponent = ({ children }:
return (
-
+
);
diff --git a/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx b/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx
index d662db58b..76d710cb7 100644
--- a/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx
+++ b/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx
@@ -1,5 +1,6 @@
import { Icon, Label, Stack } from "@fluentui/react";
import * as React from "react";
+import { NormalizedEventKey } from "../../../Common/Constants";
import { accordionStackTokens } from "../Settings/SettingsRenderUtils";
export interface CollapsibleSectionProps {
@@ -30,6 +31,13 @@ export class CollapsibleSectionComponent extends React.Component {
+ if (event.key === NormalizedEventKey.Space || event.key === NormalizedEventKey.Enter) {
+ this.toggleCollapsed();
+ event.stopPropagation();
+ }
+ };
+
public render(): JSX.Element {
return (
<>
@@ -39,6 +47,11 @@ export class CollapsibleSectionComponent extends React.Component
diff --git a/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap b/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap
index 95d3c46bf..675a79239 100644
--- a/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap
+++ b/src/Explorer/Controls/CollapsiblePanel/__snapshots__/CollapsibleSectionComponent.test.tsx.snap
@@ -3,9 +3,14 @@
exports[`CollapsibleSectionComponent renders 1`] = `
- this.setState({ isSuggestionVisible: true })}
- onChange={(_event, newValue?: string) => this.handleChange(newValue)}
- />
+
{this.props.showCancelButton && (
1`] = `
-
+
`;
@@ -28,16 +34,22 @@ exports[`inputTypeahead renders 1`] = `
-
+
`;
diff --git a/src/Explorer/Controls/Settings/SettingsRenderUtils.tsx b/src/Explorer/Controls/Settings/SettingsRenderUtils.tsx
index c0cda4f59..11f00f195 100644
--- a/src/Explorer/Controls/Settings/SettingsRenderUtils.tsx
+++ b/src/Explorer/Controls/Settings/SettingsRenderUtils.tsx
@@ -1,45 +1,45 @@
-import * as React from "react";
-import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
-import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
-import { Urls, StyleConstants } from "../../../Common/Constants";
import {
- getPriceCurrency,
- getCurrencySign,
- getAutoscalePricePerRu,
- getMultimasterMultiplier,
- computeRUUsagePriceHourly,
- getPricePerRu,
- estimatedCostDisclaimer,
-} from "../../../Utils/PricingUtils";
-import {
- ITextFieldStyles,
+ DetailsList,
+ DetailsListLayoutMode,
+ DetailsRow,
ICheckboxStyles,
- IStackProps,
- IStackTokens,
IChoiceGroupStyles,
- Link,
- Text,
- IMessageBarStyles,
- ITextStyles,
- IDetailsRowStyles,
- IStackStyles,
+ IColumn,
+ IDetailsColumnStyles,
IDetailsListStyles,
+ IDetailsRowProps,
+ IDetailsRowStyles,
IDropdownStyles,
+ IMessageBarStyles,
ISeparatorStyles,
+ IStackProps,
+ IStackStyles,
+ IStackTokens,
+ ITextFieldStyles,
+ ITextStyles,
+ Link,
MessageBar,
MessageBarType,
- Stack,
+ SelectionMode,
Spinner,
SpinnerSize,
- DetailsList,
- IColumn,
- SelectionMode,
- DetailsListLayoutMode,
- IDetailsRowProps,
- DetailsRow,
- IDetailsColumnStyles,
+ Stack,
+ Text,
} from "@fluentui/react";
-import { isDirtyTypes, isDirty } from "./SettingsUtils";
+import * as React from "react";
+import { StyleConstants, Urls } from "../../../Common/Constants";
+import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
+import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
+import {
+ computeRUUsagePriceHourly,
+ estimatedCostDisclaimer,
+ getAutoscalePricePerRu,
+ getCurrencySign,
+ getMultimasterMultiplier,
+ getPriceCurrency,
+ getPricePerRu,
+} from "../../../Utils/PricingUtils";
+import { isDirty, isDirtyTypes } from "./SettingsUtils";
export interface EstimatedSpendingDisplayProps {
costType: JSX.Element;
@@ -65,7 +65,7 @@ export interface PriceBreakdown {
currencySign: string;
}
-export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14 } };
+export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "windowtext" } };
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
label: {
@@ -223,14 +223,15 @@ export const getRuPriceBreakdown = (
multimasterEnabled: isMultimaster,
isAutoscale: isAutoscale,
});
- const basePricePerRu: number = isAutoscale
- ? getAutoscalePricePerRu(serverId, getMultimasterMultiplier(numberOfRegions, isMultimaster))
- : getPricePerRu(serverId);
+ const multimasterMultiplier = getMultimasterMultiplier(numberOfRegions, isMultimaster);
+ const pricePerRu: number = isAutoscale
+ ? getAutoscalePricePerRu(serverId, multimasterMultiplier)
+ : getPricePerRu(serverId, multimasterMultiplier);
return {
- hourlyPrice: hourlyPrice,
+ hourlyPrice,
dailyPrice: hourlyPrice * 24,
monthlyPrice: hourlyPrice * hoursInAMonth,
- pricePerRu: basePricePerRu * getMultimasterMultiplier(numberOfRegions, isMultimaster),
+ pricePerRu,
currency: getPriceCurrency(serverId),
currencySign: getCurrencySign(serverId),
};
@@ -271,7 +272,7 @@ export const manualToAutoscaleDisclaimerElement: JSX.Element = (
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings
and storage of your resource. After autoscale has been enabled, you can change the max RU/s.{" "}
- Learn more
+ Learn more
);
diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/IndexingPolicyRefresh/__snapshots__/IndexingPolicyRefreshComponent.test.tsx.snap b/src/Explorer/Controls/Settings/SettingsSubComponents/IndexingPolicyRefresh/__snapshots__/IndexingPolicyRefreshComponent.test.tsx.snap
index 4912cdd65..6210e1e95 100644
--- a/src/Explorer/Controls/Settings/SettingsSubComponents/IndexingPolicyRefresh/__snapshots__/IndexingPolicyRefreshComponent.test.tsx.snap
+++ b/src/Explorer/Controls/Settings/SettingsSubComponents/IndexingPolicyRefresh/__snapshots__/IndexingPolicyRefreshComponent.test.tsx.snap
@@ -8,6 +8,7 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
styles={
Object {
"root": Object {
+ "color": "windowtext",
"fontSize": 14,
},
}
diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/__snapshots__/ThroughputInputAutoPilotV3Component.test.tsx.snap b/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/__snapshots__/ThroughputInputAutoPilotV3Component.test.tsx.snap
index 43bdce840..db2a3576f 100644
--- a/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/__snapshots__/ThroughputInputAutoPilotV3Component.test.tsx.snap
+++ b/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/__snapshots__/ThroughputInputAutoPilotV3Component.test.tsx.snap
@@ -20,6 +20,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={
Object {
"root": Object {
+ "color": "windowtext",
"fontSize": 14,
},
}
@@ -39,6 +40,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={
Object {
"root": Object {
+ "color": "windowtext",
"fontSize": 14,
},
}
@@ -73,6 +75,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={
Object {
"root": Object {
+ "color": "windowtext",
"fontSize": 14,
},
}
@@ -80,11 +83,11 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
>
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s.
-
Learn more
-
+
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s.
-
Learn more
-
+
= ({
const currency: string = getPriceCurrency(serverId);
const currencySign: string = getCurrencySign(serverId);
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
- const pricePerRu = isAutoscale
- ? getAutoscalePricePerRu(serverId, multiplier) * multiplier
- : getPricePerRu(serverId) * multiplier;
+ const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
- const iconWithEstimatedCostDisclaimer: JSX.Element = PricingUtils.estimatedCostDisclaimer;
+ const iconWithEstimatedCostDisclaimer: JSX.Element = {estimatedCostDisclaimer};
if (isAutoscale) {
return (
diff --git a/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx b/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx
index 8c780cebc..de8b7757a 100644
--- a/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx
+++ b/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx
@@ -118,6 +118,7 @@ export const ThroughputInput: FunctionComponent = ({
= ({
aria-label="Manual mode"
checked={!isAutoscaleSelected}
type="radio"
+ aria-required={true}
role="radio"
tabIndex={0}
onChange={(e) => handleOnChangeMode(e, "Manual")}
diff --git a/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap b/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap
index d295a7574..0798c01ef 100644
--- a/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap
+++ b/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap
@@ -345,12 +345,14 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
ariaLabel="Info"
className="panelInfoIcon"
iconName="Info"
+ tabIndex={0}
>
@@ -651,6 +654,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
>
diff --git a/src/Explorer/Controls/TreeComponent/TreeComponent.tsx b/src/Explorer/Controls/TreeComponent/TreeComponent.tsx
index 6c5ceb817..c54d8fcf4 100644
--- a/src/Explorer/Controls/TreeComponent/TreeComponent.tsx
+++ b/src/Explorer/Controls/TreeComponent/TreeComponent.tsx
@@ -243,6 +243,7 @@ export class TreeNodeComponent extends React.Component
@@ -423,6 +424,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
},
}
}
+ title="More"
/>
diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx
index 2cd3e79da..55f056df7 100644
--- a/src/Explorer/Explorer.tsx
+++ b/src/Explorer/Explorer.tsx
@@ -4,7 +4,6 @@ import _ from "underscore";
import { AuthType } from "../AuthType";
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
import * as Constants from "../Common/Constants";
-import { ConnectionStatusType } from "../Common/Constants";
import { readCollection } from "../Common/dataAccess/readCollection";
import { readDatabases } from "../Common/dataAccess/readDatabases";
import { isPublicInternetAccessAllowed } from "../Common/DatabaseAccountUtility";
@@ -347,10 +346,6 @@ export default class Explorer {
}
this._isInitializingNotebooks = true;
if (userContext.features.phoenix) {
- const connectionStatus: DataModels.ContainerConnectionInfo = {
- status: ConnectionStatusType.Allocating,
- };
- useNotebook.getState().setConnectionInfo(connectionStatus);
const provisionData = {
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
resourceId: userContext.databaseAccount.id,
@@ -361,9 +356,6 @@ export default class Explorer {
};
const connectionInfo = await this.phoenixClient.containerConnectionInfo(provisionData);
if (connectionInfo.data && connectionInfo.data.notebookServerUrl) {
- connectionStatus.status = ConnectionStatusType.Connected;
- useNotebook.getState().setConnectionInfo(connectionStatus);
-
useNotebook.getState().setNotebookServerInfo({
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
diff --git a/src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx b/src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
index ff46f357f..c8a700976 100644
--- a/src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
+++ b/src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
@@ -1,8 +1,8 @@
import * as React from "react";
-import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
import CollapseArrowIcon from "../../../../images/Collapse_arrow_14x14.svg";
import ExpandIcon from "../../../../images/Expand_14x14.svg";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
+import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
interface MiddlePaneComponentProps {
isTabsContentExpanded: boolean;
@@ -17,7 +17,14 @@ export class MiddlePaneComponent extends React.Component