mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 23:16:56 +00:00
Add check and guidance for networking settings (#1348)
This commit is contained in:
parent
5b1db2778c
commit
5dde66b032
@ -34,6 +34,7 @@ export interface DatabaseAccountExtendedProperties {
|
|||||||
capacity?: { totalThroughputLimit: number };
|
capacity?: { totalThroughputLimit: number };
|
||||||
locations?: DatabaseAccountResponseLocation[];
|
locations?: DatabaseAccountResponseLocation[];
|
||||||
postgresqlEndpoint?: string;
|
postgresqlEndpoint?: string;
|
||||||
|
publicNetworkAccess?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseAccountResponseLocation {
|
export interface DatabaseAccountResponseLocation {
|
||||||
|
@ -37,6 +37,7 @@ export enum MessageTypes {
|
|||||||
OpenQuickstartBlade,
|
OpenQuickstartBlade,
|
||||||
OpenPostgreSQLPasswordReset,
|
OpenPostgreSQLPasswordReset,
|
||||||
OpenPostgresNetworkingBlade,
|
OpenPostgresNetworkingBlade,
|
||||||
|
OpenCosmosDBNetworkingBlade,
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Versions, ActionContracts, Diagnostics };
|
export { Versions, ActionContracts, Diagnostics };
|
||||||
|
@ -186,7 +186,6 @@ export interface Collection extends CollectionBase {
|
|||||||
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||||
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
||||||
|
|
||||||
getLabel(): string;
|
|
||||||
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { MessageBar, MessageBarButton, MessageBarType } from "@fluentui/react";
|
||||||
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
import { CollectionTabKind } from "Contracts/ViewModels";
|
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";
|
||||||
@ -21,10 +24,24 @@ interface TabsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||||
const { openedTabs, openedReactTabs, activeTab, activeReactTab } = useTabs();
|
const { openedTabs, openedReactTabs, activeTab, activeReactTab, showNetworkSettingsWarning } = useTabs();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tabsManagerContainer">
|
<div className="tabsManagerContainer">
|
||||||
|
{showNetworkSettingsWarning && (
|
||||||
|
<MessageBar
|
||||||
|
messageBarType={MessageBarType.warning}
|
||||||
|
actions={
|
||||||
|
<MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenCosmosDBNetworkingBlade })}>
|
||||||
|
Change network settings
|
||||||
|
</MessageBarButton>
|
||||||
|
}
|
||||||
|
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||||
|
>
|
||||||
|
The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure
|
||||||
|
Portal to proceed.
|
||||||
|
</MessageBar>
|
||||||
|
)}
|
||||||
<div id="content" className="flexContainer hideOverflows">
|
<div id="content" className="flexContainer hideOverflows">
|
||||||
<div className="nav-tabs-margin">
|
<div className="nav-tabs-margin">
|
||||||
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
|
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
|
||||||
|
@ -1160,23 +1160,6 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
this.onDocumentDBDocumentsClick();
|
this.onDocumentDBDocumentsClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get correct collection label depending on account API
|
|
||||||
*/
|
|
||||||
public getLabel(): string {
|
|
||||||
if (userContext.apiType === "Tables") {
|
|
||||||
return "Entities";
|
|
||||||
} else if (userContext.apiType === "Cassandra") {
|
|
||||||
return "Rows";
|
|
||||||
} else if (userContext.apiType === "Gremlin") {
|
|
||||||
return "Graph";
|
|
||||||
} else if (userContext.apiType === "Mongo") {
|
|
||||||
return "Documents";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Items";
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDatabase(): ViewModels.Database {
|
public getDatabase(): ViewModels.Database {
|
||||||
return useDatabases.getState().findDatabaseWithId(this.databaseId);
|
return useDatabases.getState().findDatabaseWithId(this.databaseId);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { getItemName } from "Utils/APITypeUtils";
|
||||||
import shallow from "zustand/shallow";
|
import shallow from "zustand/shallow";
|
||||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||||
import DeleteIcon from "../../../images/delete.svg";
|
import DeleteIcon from "../../../images/delete.svg";
|
||||||
@ -497,7 +498,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
|||||||
const buildCollectionNode = (database: ViewModels.Database, collection: ViewModels.Collection): TreeNode => {
|
const buildCollectionNode = (database: ViewModels.Database, collection: ViewModels.Collection): TreeNode => {
|
||||||
const children: TreeNode[] = [];
|
const children: TreeNode[] = [];
|
||||||
children.push({
|
children.push({
|
||||||
label: collection.getLabel(),
|
label: getItemName(),
|
||||||
id: collection.isSampleCollection ? "sampleItems" : "",
|
id: collection.isSampleCollection ? "sampleItems" : "",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
collection.openTab();
|
collection.openTab();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { getItemName } from "Utils/APITypeUtils";
|
||||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||||
import DeleteIcon from "../../../images/delete.svg";
|
import DeleteIcon from "../../../images/delete.svg";
|
||||||
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
||||||
@ -254,7 +255,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
private buildCollectionNode(database: ViewModels.Database, collection: ViewModels.Collection): TreeNode {
|
private buildCollectionNode(database: ViewModels.Database, collection: ViewModels.Collection): TreeNode {
|
||||||
const children: TreeNode[] = [];
|
const children: TreeNode[] = [];
|
||||||
children.push({
|
children.push({
|
||||||
label: collection.getLabel(),
|
label: getItemName(),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
collection.openTab();
|
collection.openTab();
|
||||||
// push to most recent
|
// push to most recent
|
||||||
|
@ -74,3 +74,18 @@ export const getApiShortDisplayName = (): string => {
|
|||||||
return "Table API";
|
return "Table API";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getItemName = (): string => {
|
||||||
|
switch (userContext.apiType) {
|
||||||
|
case "Tables":
|
||||||
|
return "Entities";
|
||||||
|
case "Cassandra":
|
||||||
|
return "Rows";
|
||||||
|
case "Gremlin":
|
||||||
|
return "Graph";
|
||||||
|
case "Mongo":
|
||||||
|
return "Documents";
|
||||||
|
default:
|
||||||
|
return "Items";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
40
src/Utils/NetworkUtility.ts
Normal file
40
src/Utils/NetworkUtility.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { userContext } from "UserContext";
|
||||||
|
|
||||||
|
const PortalIPs: { [key: string]: string[] } = {
|
||||||
|
prod1: ["104.42.195.92", "40.76.54.131"],
|
||||||
|
prod2: ["104.42.196.69"],
|
||||||
|
mooncake: ["139.217.8.252"],
|
||||||
|
blackforest: ["51.4.229.218"],
|
||||||
|
fairfax: ["52.244.48.71"],
|
||||||
|
ussec: ["29.26.26.67", "29.26.26.66"],
|
||||||
|
usnat: ["7.28.202.68"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doNetworkSettingsAllowDataExplorerAccess = (): boolean => {
|
||||||
|
const accountProperties = userContext.databaseAccount?.properties;
|
||||||
|
|
||||||
|
if (!accountProperties) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public network access is disabled
|
||||||
|
if (accountProperties.publicNetworkAccess !== "Enabled") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipRules = accountProperties.ipRules;
|
||||||
|
// public network access is set to "All networks"
|
||||||
|
if (ipRules.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const portalIPs = PortalIPs[userContext.portalEnv];
|
||||||
|
let numberOfMatches = 0;
|
||||||
|
ipRules.forEach((ipRule) => {
|
||||||
|
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
|
||||||
|
numberOfMatches++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return numberOfMatches === portalIPs.length;
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { doNetworkSettingsAllowDataExplorerAccess } from "Utils/NetworkUtility";
|
||||||
import { applyExplorerBindings } from "../applyExplorerBindings";
|
import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { AccountKind, Flights } from "../Common/Constants";
|
import { AccountKind, Flights } from "../Common/Constants";
|
||||||
@ -381,6 +382,8 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useTabs.getState().setShowNetworkSettingsWarning(!doNetworkSettingsAllowDataExplorerAccess());
|
||||||
|
|
||||||
if (inputs.features) {
|
if (inputs.features) {
|
||||||
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));
|
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ interface TabsState {
|
|||||||
openedReactTabs: ReactTabKind[];
|
openedReactTabs: ReactTabKind[];
|
||||||
activeTab: TabsBase | undefined;
|
activeTab: TabsBase | undefined;
|
||||||
activeReactTab: ReactTabKind | undefined;
|
activeReactTab: ReactTabKind | undefined;
|
||||||
|
showNetworkSettingsWarning: boolean;
|
||||||
activateTab: (tab: TabsBase) => void;
|
activateTab: (tab: TabsBase) => void;
|
||||||
activateNewTab: (tab: TabsBase) => void;
|
activateNewTab: (tab: TabsBase) => void;
|
||||||
activateReactTab: (tabkind: ReactTabKind) => void;
|
activateReactTab: (tabkind: ReactTabKind) => void;
|
||||||
@ -20,6 +21,7 @@ interface TabsState {
|
|||||||
closeAllNotebookTabs: (hardClose: boolean) => void;
|
closeAllNotebookTabs: (hardClose: boolean) => void;
|
||||||
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
||||||
closeReactTab: (tabKind: ReactTabKind) => void;
|
closeReactTab: (tabKind: ReactTabKind) => void;
|
||||||
|
setShowNetworkSettingsWarning: (showWarning: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ReactTabKind {
|
export enum ReactTabKind {
|
||||||
@ -33,6 +35,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|||||||
openedReactTabs: [ReactTabKind.Home],
|
openedReactTabs: [ReactTabKind.Home],
|
||||||
activeTab: undefined,
|
activeTab: undefined,
|
||||||
activeReactTab: ReactTabKind.Home,
|
activeReactTab: ReactTabKind.Home,
|
||||||
|
showNetworkSettingsWarning: false,
|
||||||
activateTab: (tab: TabsBase): void => {
|
activateTab: (tab: TabsBase): void => {
|
||||||
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
||||||
set({ activeTab: tab, activeReactTab: undefined });
|
set({ activeTab: tab, activeReactTab: undefined });
|
||||||
@ -142,4 +145,5 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|||||||
|
|
||||||
set({ openedReactTabs: updatedOpenedReactTabs });
|
set({ openedReactTabs: updatedOpenedReactTabs });
|
||||||
},
|
},
|
||||||
|
setShowNetworkSettingsWarning: (showWarning: boolean) => set({ showNetworkSettingsWarning: showWarning }),
|
||||||
}));
|
}));
|
||||||
|
Loading…
Reference in New Issue
Block a user