Merge branch 'master' into users/languy/save-documentstab-prefs

This commit is contained in:
Laurent Nguyen 2024-09-05 09:41:58 +02:00
commit 1795b8e2e9
26 changed files with 722 additions and 179 deletions

View File

@ -1906,8 +1906,14 @@ input::-webkit-calendar-picker-indicator::after {
}
.nav-tabs-margin {
padding-top: 5px;
height: 32px;
background-color: #f2f2f2;
.nav-tabs {
display: flex;
align-items: flex-end;
height: 100%;
}
}
.navTabHeight {

View File

@ -134,6 +134,8 @@ export class BackendApi {
public static readonly GenerateToken: string = "GenerateToken";
public static readonly PortalSettings: string = "PortalSettings";
public static readonly AccountRestrictions: string = "AccountRestrictions";
public static readonly RuntimeProxy: string = "RuntimeProxy";
public static readonly DisallowedLocations: string = "DisallowedLocations";
}
export class PortalBackendEndpoints {
@ -183,6 +185,12 @@ export class CassandraProxyAPIs {
public static readonly connectionStringSchemaApi: string = "api/connectionstring/cassandra/schema";
}
export class AadEndpoints {
public static readonly Prod: string = "https://login.microsoftonline.com/";
public static readonly Fairfax: string = "https://login.microsoftonline.us/";
public static readonly Mooncake: string = "https://login.partner.microsoftonline.cn/";
}
export class Queries {
public static CustomPageOption: string = "custom";
public static UnlimitedPageOption: string = "unlimited";

View File

@ -3,15 +3,16 @@ import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizatio
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
import { AuthType } from "../AuthType";
import { PriorityLevel } from "../Common/Constants";
import { BackendApi, PriorityLevel } from "../Common/Constants";
import * as Logger from "../Common/Logger";
import { Platform, configContext } from "../ConfigContext";
import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
import { getErrorMessage } from "./ErrorHandlingUtils";
import * as Logger from "../Common/Logger";
const _global = typeof self === "undefined" ? window : self;
@ -123,6 +124,37 @@ export async function getTokenFromAuthService(
verb: string,
resourceType: string,
resourceId?: string,
): Promise<AuthorizationToken> {
if (!useNewPortalBackendEndpoint(BackendApi.RuntimeProxy)) {
return getTokenFromAuthService_ToBeDeprecated(verb, resourceType, resourceId);
}
try {
const host: string = configContext.PORTAL_BACKEND_ENDPOINT;
const response: Response = await _global.fetch(host + "/api/connectionstring/runtimeproxy/authorizationtokens", {
method: "POST",
headers: {
"content-type": "application/json",
"x-ms-encrypted-auth-token": userContext.accessToken,
},
body: JSON.stringify({
verb,
resourceType,
resourceId,
}),
});
const result: AuthorizationToken = await response.json();
return result;
} catch (error) {
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${getErrorMessage(error)}`);
return Promise.reject(error);
}
}
export async function getTokenFromAuthService_ToBeDeprecated(
verb: string,
resourceType: string,
resourceId?: string,
): Promise<AuthorizationToken> {
try {
const host = configContext.BACKEND_ENDPOINT;

View File

@ -720,7 +720,8 @@ export function useMongoProxyEndpoint(api: string): boolean {
MongoProxyEndpoints.Local,
MongoProxyEndpoints.Mpac,
MongoProxyEndpoints.Prod,
// MongoProxyEndpoints.Fairfax,
MongoProxyEndpoints.Fairfax,
MongoProxyEndpoints.Mooncake,
];
let canAccessMongoProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
if (

View File

@ -87,7 +87,7 @@ let configContext: Readonly<ConfigContext> = {
`^https:\\/\\/.*\\.analysis-df\\.net$`,
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
`^https:\\/\\/.*\\.azure-test\\.net$`,
`^https:\\/\\/cosmos-explorer-preview\\.azurewebsites\\.net`,
`^https:\\/\\/cosmos-explorer-preview\\.azurewebsites\\.net$`,
], // Webpack injects this at build time
gitSha: process.env.GIT_SHA,
hostedExplorerURL: "https://cosmos.azure.com/",

View File

@ -17,7 +17,7 @@ export const useTreeStyles = makeStyles({
minWidth: "100%",
rowGap: "0px",
paddingTop: "0px",
[treeIconWidth]: "20px",
[treeIconWidth]: "16px",
[leafNodeSpacing]: "24px",
},
nodeIcon: {
@ -32,7 +32,6 @@ export const useTreeStyles = makeStyles({
fontSize: tokens.fontSizeBase300,
height: tokens.layoutRowHeight,
...cosmosShorthands.borderBottom(),
paddingLeft: `calc(var(${treeItemLevelToken}, 1) * ${tokens.spacingHorizontalXXL})`,
// Some sneaky CSS variables stuff to change the background color of the action button on hover.
[actionButtonBackground]: tokens.colorNeutralBackground1,

View File

@ -149,15 +149,16 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
// We use the expandIcon slot to hold the node icon too.
// We only show a node icon for leaf nodes, even if a branch node has an iconSrc.
const expandIcon = isLoading ? (
<Spinner size="extra-tiny" />
) : !isBranch ? (
typeof node.iconSrc === "string" ? (
const treeIcon =
node.iconSrc === undefined ? undefined : typeof node.iconSrc === "string" ? (
<img src={node.iconSrc} className={treeStyles.nodeIcon} alt="" />
) : (
node.iconSrc
)
) : openItems.includes(treeNodeId) ? (
);
const expandIcon = isLoading ? (
<Spinner size="extra-tiny" />
) : !isBranch ? undefined : openItems.includes(treeNodeId) ? (
<ChevronDown20Regular data-test="TreeNode/CollapseIcon" />
) : (
<ChevronRight20Regular data-text="TreeNode/ExpandIcon" />
@ -174,7 +175,6 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
<TreeItemLayout
className={mergeClasses(
treeStyles.treeItemLayout,
expandIcon ? undefined : treeStyles.treeItemLayoutNoIcon,
shouldShowAsSelected && treeStyles.selectedItem,
node.className && treeStyles[node.className],
)}
@ -200,6 +200,7 @@ export const TreeNodeComponent: React.FC<TreeNodeComponentProps> = ({
),
}
}
iconBefore={treeIcon}
expandIcon={expandIcon}
>
<span className={treeStyles.nodeLabel}>{node.label}</span>

View File

@ -10,13 +10,20 @@ exports[`TreeNodeComponent does not render children if the node is loading 1`] =
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
}
>
<span
className="___1h29e9h_0000000 fz5stix"
@ -156,7 +163,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
"itemType": "branch",
"layoutRef": {
"current": <div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
>
<div
@ -179,6 +186,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -208,7 +225,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="-1"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
>
<div
@ -231,6 +248,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -242,7 +269,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</div>
</div>
<div
class="fui-Tree rnv2ez3 ___jy13a00_lpffjy0 f1acs6jw f11qra4b fepn2xe f1nbblvp fhxm7u5 fzz4f4n"
class="fui-Tree rnv2ez3 ___17a32do_7zrvj80 f1acs6jw f11qra4b fepn2xe f1nbblvp f19d5ny4 fzz4f4n"
data-test="Tree:root"
role="tree"
>
@ -256,7 +283,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="0"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child1Label"
>
<div
@ -279,6 +306,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child1Icon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -300,7 +337,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="-1"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child2LoadingLabel"
>
<div
@ -323,6 +360,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child2LoadingIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -343,7 +390,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="-1"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vrg09j fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_vz3p260 fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child3ExpandingLabel"
>
<div
@ -363,6 +410,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</span>
</div>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child3ExpandingIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -383,16 +440,23 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
}
>
<div
className="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
className="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
>
<div
@ -419,6 +483,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</svg>
</ChevronRight20Regular>
</div>
<div
aria-hidden={true}
className="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
</div>
<div
className="fui-TreeItemLayout__main rklbe47"
>
@ -431,7 +505,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</div>
</TreeItemLayout>
<Tree
className="___jy13a00_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp fhxm7u5 fzz4f4n"
className="___17a32do_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp f19d5ny4 fzz4f4n"
data-test="Tree:root"
>
<TreeProvider
@ -499,7 +573,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
}
>
<div
className="fui-Tree rnv2ez3 ___jy13a00_lpffjy0 f1acs6jw f11qra4b fepn2xe f1nbblvp fhxm7u5 fzz4f4n"
className="fui-Tree rnv2ez3 ___17a32do_7zrvj80 f1acs6jw f11qra4b fepn2xe f1nbblvp f19d5ny4 fzz4f4n"
data-test="Tree:root"
role="tree"
>
@ -587,7 +661,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
"itemType": "branch",
"layoutRef": {
"current": <div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child1Label"
>
<div
@ -610,6 +684,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child1Icon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -639,7 +723,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="0"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child1Label"
>
<div
@ -662,6 +746,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child1Icon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -680,16 +774,23 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child1Label"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child1Icon"
/>
}
>
<div
className="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
className="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child1Label"
>
<div
@ -716,6 +817,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</svg>
</ChevronRight20Regular>
</div>
<div
aria-hidden={true}
className="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child1Icon"
/>
</div>
<div
className="fui-TreeItemLayout__main rklbe47"
>
@ -728,7 +839,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</div>
</TreeItemLayout>
<Tree
className="___jy13a00_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp fhxm7u5 fzz4f4n"
className="___17a32do_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp f19d5ny4 fzz4f4n"
data-test="Tree:root/child1Label"
>
<TreeProvider
@ -821,7 +932,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
"itemType": "branch",
"layoutRef": {
"current": <div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child2LoadingLabel"
>
<div
@ -844,6 +955,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child2LoadingIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -873,7 +994,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="-1"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child2LoadingLabel"
>
<div
@ -896,6 +1017,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
/>
</svg>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child2LoadingIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -914,16 +1045,23 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child2LoadingLabel"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child2LoadingIcon"
/>
}
>
<div
className="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vtp8mg fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
className="fui-TreeItemLayout r1bx0xiv ___9uolwu0_9b0r4g0 fk6fouc fkhj508 figsok6 f1i3iumi fo100m9 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child2LoadingLabel"
>
<div
@ -950,6 +1088,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</svg>
</ChevronRight20Regular>
</div>
<div
aria-hidden={true}
className="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child2LoadingIcon"
/>
</div>
<div
className="fui-TreeItemLayout__main rklbe47"
>
@ -1039,7 +1187,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
"itemType": "leaf",
"layoutRef": {
"current": <div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vrg09j fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_vz3p260 fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child3ExpandingLabel"
>
<div
@ -1059,6 +1207,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</span>
</div>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child3ExpandingIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -1087,7 +1245,7 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
tabindex="-1"
>
<div
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vrg09j fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
class="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_vz3p260 fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child3ExpandingLabel"
>
<div
@ -1107,6 +1265,16 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
</span>
</div>
</div>
<div
aria-hidden="true"
class="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
class="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="child3ExpandingIcon"
/>
</div>
<div
class="fui-TreeItemLayout__main rklbe47"
>
@ -1125,9 +1293,9 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child3ExpandingLabel"
expandIcon={
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
@ -1136,12 +1304,12 @@ exports[`TreeNodeComponent fully renders a tree 1`] = `
}
>
<div
className="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_1vrg09j fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
className="fui-TreeItemLayout r1bx0xiv ___dxcrnh0_vz3p260 fk6fouc fkhj508 figsok6 f1i3iumi f1k1erfc fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root/child3ExpandingLabel"
>
<div
aria-hidden={true}
className="fui-TreeItemLayout__expandIcon rh4pu5o"
className="fui-TreeItemLayout__iconBefore rphzgg1 ___1lqnc2u_1hdcey2 f7x41pl"
>
<img
alt=""
@ -1184,9 +1352,9 @@ exports[`TreeNodeComponent renders a loading spinner if the node is loading: loa
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
@ -1213,13 +1381,20 @@ exports[`TreeNodeComponent renders a loading spinner if the node is loading: loa
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
<Spinner
size="extra-tiny"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
}
>
<span
className="___1h29e9h_0000000 fz5stix"
@ -1240,13 +1415,20 @@ exports[`TreeNodeComponent renders a node as expandable if it has empty, but def
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
}
>
<span
className="___1h29e9h_0000000 fz5stix"
@ -1313,9 +1495,9 @@ exports[`TreeNodeComponent renders a node with a menu 1`] = `
"className": "___1r8p62d_0000000 f1xg1ack f1e31b4d",
}
}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
@ -1363,9 +1545,9 @@ exports[`TreeNodeComponent renders a single node 1`] = `
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
@ -1392,9 +1574,9 @@ exports[`TreeNodeComponent renders an icon if the node has one 1`] = `
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
@ -1421,13 +1603,20 @@ exports[`TreeNodeComponent renders selected parent node as selected if no descen
>
<TreeItemLayout
actions={false}
className="___kqkdor0_ihxn0o0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1nfm20t f1do9gdl"
className="___rq9vxg0_1ykn2d2 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1nfm20t f1do9gdl"
data-test="TreeNode:root"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
}
>
<span
className="___1h29e9h_0000000 fz5stix"
@ -1436,7 +1625,7 @@ exports[`TreeNodeComponent renders selected parent node as selected if no descen
</span>
</TreeItemLayout>
<Tree
className="___jy13a00_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp fhxm7u5 fzz4f4n"
className="___17a32do_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp f19d5ny4 fzz4f4n"
data-test="Tree:root"
>
<TreeNodeComponent
@ -1497,13 +1686,20 @@ exports[`TreeNodeComponent renders selected parent node as unselected if any des
>
<TreeItemLayout
actions={false}
className="___1kqyw53_iy2icj0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1do9gdl"
className="___z7owk70_14ep1pe fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1do9gdl"
data-test="TreeNode:root"
expandIcon={
<ChevronRight20Regular
data-text="TreeNode/ExpandIcon"
/>
}
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"
src="rootIcon"
/>
}
>
<span
className="___1h29e9h_0000000 fz5stix"
@ -1512,7 +1708,7 @@ exports[`TreeNodeComponent renders selected parent node as unselected if any des
</span>
</TreeItemLayout>
<Tree
className="___jy13a00_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp fhxm7u5 fzz4f4n"
className="___17a32do_0000000 f1acs6jw f11qra4b fepn2xe f1nbblvp f19d5ny4 fzz4f4n"
data-test="Tree:root"
>
<TreeNodeComponent
@ -1574,9 +1770,9 @@ exports[`TreeNodeComponent renders single selected leaf node as selected 1`] = `
>
<TreeItemLayout
actions={false}
className="___kqkdor0_ihxn0o0 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1k1erfc f1n8cmsf f1ktbui8 f1nfm20t f1do9gdl"
className="___rq9vxg0_1ykn2d2 fkhj508 fbv8p0b f1f09k3d fg706s2 frpde29 f1n8cmsf f1ktbui8 f1nfm20t f1do9gdl"
data-test="TreeNode:root"
expandIcon={
iconBefore={
<img
alt=""
className="___i3nbrx0_0000000 f1do9gdl fbv8p0b"

View File

@ -1,5 +1,6 @@
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { PhoenixClient } from "Phoenix/PhoenixClient";
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
import { cloneDeep } from "lodash";
import create, { UseStore } from "zustand";
import { AuthType } from "../../AuthType";
@ -127,7 +128,9 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo"
? databaseAccount?.location
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
const disallowedLocationsUri: string = useNewPortalBackendEndpoint(Constants.BackendApi.DisallowedLocations)
? `${configContext.PORTAL_BACKEND_ENDPOINT}/api/disallowedlocations`
: `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
const authorizationHeader = getAuthorizationHeader();
try {
const response = await fetch(disallowedLocationsUri, {

View File

@ -238,7 +238,7 @@ exports[`Settings Pane should render Default properly 1`] = `
},
]
}
selectedKey="vertical"
selectedKey="horizontal"
styles={
{
"flexContainer": [

View File

@ -86,7 +86,7 @@ const useSidebarStyles = makeStyles({
},
},
globalCommandsMenuButton: {
display: "initial",
display: "inline-flex",
"@container (min-width: 250px)": {
display: "none",
},
@ -280,7 +280,7 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
{/* Collections Tree - Start */}
{hasSidebar && (
// When collapsed, we force the pane to 24 pixels wide and make it non-resizable.
<Allotment.Pane minSize={24} preferredSize={300}>
<Allotment.Pane minSize={24} preferredSize={250}>
<CosmosFluentProvider className={mergeClasses(styles.sidebar)}>
<div className={styles.sidebarContainer}>
{loading && (

View File

@ -523,7 +523,10 @@ export interface IDocumentsTabComponentProps {
const getUniqueId = (collection: ViewModels.CollectionBase): string => `${collection.databaseId}-${collection.id()}`;
const defaultSqlFilters = ['WHERE c.id = "foo"', "ORDER BY c._ts DESC", 'WHERE c.id = "foo" ORDER BY c._ts DESC'];
const getDefaultSqlFilters = (partitionKeys: string[]) =>
['WHERE c.id = "foo"', "ORDER BY c._ts DESC", 'WHERE c.id = "foo" ORDER BY c._ts DESC', "ORDER BY c._ts ASC"].concat(
partitionKeys.map((partitionKey) => `WHERE c.${partitionKey} = "foo"`),
);
const defaultMongoFilters = ['{"id":"foo"}', "{ qty: { $gte: 20 } }"];
// Extend DocumentId to include fields displayed in the table
@ -1934,7 +1937,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
<datalist id={`filtersList-${getUniqueId(_collection)}`}>
{addStringsNoDuplicate(
lastFilterContents,
isPreferredApiMongoDB ? defaultMongoFilters : defaultSqlFilters,
isPreferredApiMongoDB ? defaultMongoFilters : getDefaultSqlFilters(partitionKeyProperties),
).map((filter) => (
<option key={filter} value={filter} />
))}

View File

@ -107,7 +107,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
>
<List
direction="ltr"
height={-36}
height={-32}
itemCount={3}
itemData={
[
@ -146,7 +146,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
},
]
}
itemSize={36}
itemSize={32}
layout="vertical"
overscanCount={2}
style={
@ -163,7 +163,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
{
"WebkitOverflowScrolling": "touch",
"direction": "ltr",
"height": -36,
"height": -32,
"overflow": "auto",
"overflowY": "scroll",
"position": "relative",
@ -175,7 +175,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
<div
style={
{
"height": 108,
"height": 96,
"pointerEvents": undefined,
"width": "100%",
}
@ -223,7 +223,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
key="0"
style={
{
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
@ -241,7 +241,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
@ -259,7 +259,7 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
@ -313,11 +313,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
key="1"
style={
{
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 36,
"top": 32,
"width": "100%",
}
}
@ -331,11 +331,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 36,
"top": 32,
"userSelect": "none",
"width": "100%",
}
@ -349,11 +349,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 36,
"top": 32,
"userSelect": "none",
"width": "100%",
}
@ -403,11 +403,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
key="2"
style={
{
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 72,
"top": 64,
"width": "100%",
}
}
@ -421,11 +421,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 72,
"top": 64,
"userSelect": "none",
"width": "100%",
}
@ -439,11 +439,11 @@ exports[`DocumentsTableComponent should not render selection column when isSelec
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 72,
"top": 64,
"userSelect": "none",
"width": "100%",
}
@ -610,7 +610,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
>
<List
direction="ltr"
height={-36}
height={-32}
itemCount={3}
itemData={
[
@ -649,7 +649,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
},
]
}
itemSize={36}
itemSize={32}
layout="vertical"
overscanCount={2}
style={
@ -666,7 +666,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
{
"WebkitOverflowScrolling": "touch",
"direction": "ltr",
"height": -36,
"height": -32,
"overflow": "auto",
"overflowY": "scroll",
"position": "relative",
@ -678,7 +678,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
<div
style={
{
"height": 108,
"height": 96,
"pointerEvents": undefined,
"width": "100%",
}
@ -726,7 +726,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
key="0"
style={
{
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
@ -744,7 +744,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
@ -762,7 +762,7 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
@ -857,11 +857,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
key="1"
style={
{
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 36,
"top": 32,
"width": "100%",
}
}
@ -875,11 +875,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 36,
"top": 32,
"userSelect": "none",
"width": "100%",
}
@ -893,11 +893,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 36,
"top": 32,
"userSelect": "none",
"width": "100%",
}
@ -988,11 +988,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
key="2"
style={
{
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 72,
"top": 64,
"width": "100%",
}
}
@ -1006,11 +1006,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 72,
"top": 64,
"userSelect": "none",
"width": "100%",
}
@ -1024,11 +1024,11 @@ exports[`DocumentsTableComponent should render documents and partition keys in h
style={
{
"cursor": "pointer",
"height": 36,
"height": 32,
"left": 0,
"position": "absolute",
"right": undefined,
"top": 72,
"top": 64,
"userSelect": "none",
"width": "100%",
}

View File

@ -100,8 +100,7 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
},
}}
>
{`To prevent queries from using excessive RUs, Data Explorer has a 5,000 RU default limit. To modify or remove
the limit, go to the Settings cog on the right and find "RU Threshold".`}
{`Data Explorer has a 5,000 RU default limit. To adjust the limit, go to the Settings page and find "RU Threshold".`}
<Link
className="underlinedLink"
href="https://review.learn.microsoft.com/en-us/azure/cosmos-db/data-explorer?branch=main#configure-request-unit-threshold"

View File

@ -16,7 +16,7 @@ import React from "react";
import { appThemeFabricTealBrandRamp } from "../../Platform/Fabric/FabricTheme";
export const LayoutConstants = {
rowHeight: 36,
rowHeight: 32,
};
// Our CosmosFluentProvider has the same props as a FluentProvider.
@ -91,15 +91,30 @@ const appThemePortalBrandRamp: BrandVariants = {
160: "#CDD8EF",
};
const cosmosThemeElements = {
layoutRowHeight: `${LayoutConstants.rowHeight}px`,
export enum LayoutSize {
Compact,
// TODO: Cozy and Roomy layouts.
}
interface CosmosThemeElements {
layoutRowHeight: string;
}
export type CosmosTheme = Theme & CosmosThemeElements;
const sizeMappings: Record<LayoutSize, Partial<Theme> & CosmosThemeElements> = {
[LayoutSize.Compact]: {
layoutRowHeight: "32px",
fontSizeBase300: "13px",
},
};
const cosmosTheme = {
sidebarMinimumWidth: "200px",
sidebarInitialWidth: "300px",
};
export type CosmosTheme = Theme & typeof cosmosThemeElements;
export const tokens = themeToTokensObject({ ...webLightTheme, ...cosmosThemeElements });
export const tokens = themeToTokensObject({ ...webLightTheme, ...cosmosTheme, ...sizeMappings[LayoutSize.Compact] });
export const cosmosShorthands = {
border: () => shorthands.border("1px", "solid", tokens.colorNeutralStroke2),
@ -117,6 +132,7 @@ export function getPlatformTheme(platform: Platform): CosmosTheme {
return {
...baseTheme,
...cosmosThemeElements,
...cosmosTheme,
...sizeMappings[LayoutSize.Compact], // TODO: Allow for different layout sizes.
};
}

View File

@ -30,6 +30,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -69,6 +72,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -92,6 +98,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -102,6 +111,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
{
"children": [
{
"iconSrc": <SettingsRegular
fontSize={16}
/>,
"id": "",
"isSelected": [Function],
"label": "Scale",
@ -133,6 +145,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -156,6 +171,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -246,6 +264,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -274,6 +295,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -345,6 +369,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -415,6 +442,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -438,6 +468,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -448,6 +481,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
{
"children": [
{
"iconSrc": <SettingsRegular
fontSize={16}
/>,
"id": "",
"isSelected": [Function],
"label": "Scale",
@ -510,6 +546,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -533,6 +572,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -654,6 +696,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -682,6 +727,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -706,6 +754,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -724,6 +775,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -747,6 +801,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -766,6 +823,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -789,6 +849,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -808,6 +871,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"onClick": [Function],
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -836,6 +902,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -976,6 +1045,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -1076,6 +1148,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -1099,6 +1174,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -1109,6 +1187,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
{
"children": [
{
"iconSrc": <SettingsRegular
fontSize={16}
/>,
"id": "",
"isSelected": [Function],
"label": "Scale",
@ -1201,6 +1282,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -1224,6 +1308,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -1375,6 +1462,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -1403,6 +1493,9 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the SQ
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -1543,6 +1636,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardCollection",
@ -1638,6 +1734,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "conflictsCollection",
@ -1661,6 +1760,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "standardDb",
@ -1671,6 +1773,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
{
"children": [
{
"iconSrc": <SettingsRegular
fontSize={16}
/>,
"id": "",
"isSelected": [Function],
"label": "Scale",
@ -1763,6 +1868,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sampleItemsCollection",
@ -1786,6 +1894,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "sharedDatabase",
@ -1937,6 +2048,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteCollectionMenuItem",
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "schemaCollection",
@ -1965,6 +2079,9 @@ exports[`createDatabaseTreeNodes using NoSQL API on Hosted Platform creates expe
"styleClass": "deleteDatabaseMenuItem",
},
],
"iconSrc": <DatabaseRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "giganticDatabase",
@ -1986,6 +2103,9 @@ exports[`createResourceTokenTreeNodes creates the expected tree nodes 1`] = `
},
],
"className": "collectionNode",
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": true,
"isSelected": [Function],
"label": "testCollection",
@ -2021,6 +2141,9 @@ exports[`createSampleDataTreeNodes creates the expected tree nodes 1`] = `
"onClick": [Function],
},
],
"iconSrc": <DocumentMultipleRegular
fontSize={16}
/>,
"isExpanded": false,
"isSelected": [Function],
"label": "testCollection",

View File

@ -1,3 +1,4 @@
import { DatabaseRegular, DocumentMultipleRegular, SettingsRegular } from "@fluentui/react-icons";
import { TreeNode } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
import TabsBase from "Explorer/Tabs/TabsBase";
import StoredProcedure from "Explorer/Tree/StoredProcedure";
@ -7,6 +8,7 @@ import { useDatabases } from "Explorer/useDatabases";
import { getItemName } from "Utils/APITypeUtils";
import { isServerlessAccount } from "Utils/CapabilityUtils";
import { useTabs } from "hooks/useTabs";
import React from "react";
import { isPublicInternetAccessAllowed } from "../../Common/DatabaseAccountUtility";
import { Platform, configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
@ -25,6 +27,10 @@ export const shouldShowScriptNodes = (): boolean => {
);
};
const TreeDatabaseIcon = <DatabaseRegular fontSize={16} />;
const TreeSettingsIcon = <SettingsRegular fontSize={16} />;
const TreeCollectionIcon = <DocumentMultipleRegular fontSize={16} />;
export const createSampleDataTreeNodes = (sampleDataResourceTokenCollection: ViewModels.CollectionBase): TreeNode[] => {
const updatedSampleTree: TreeNode = {
label: sampleDataResourceTokenCollection.databaseId,
@ -36,6 +42,7 @@ export const createSampleDataTreeNodes = (sampleDataResourceTokenCollection: Vie
isExpanded: false,
className: "collectionNode",
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
iconSrc: TreeCollectionIcon,
onClick: () => {
useSelectedNode.getState().setSelectedNode(sampleDataResourceTokenCollection);
useCommandBar.getState().setContextButtons([]);
@ -104,6 +111,7 @@ export const createResourceTokenTreeNodes = (collection: ViewModels.CollectionBa
isExpanded: true,
children,
className: "collectionNode",
iconSrc: TreeCollectionIcon,
onClick: () => {
// Rewritten version of expandCollapseCollection
useSelectedNode.getState().setSelectedNode(collection);
@ -133,6 +141,7 @@ export const createDatabaseTreeNodes = (
databaseNode.children.push({
id: database.isSampleDB ? "sampleScaleSettings" : "",
label: "Scale",
iconSrc: TreeSettingsIcon,
isSelected: () =>
useSelectedNode
.getState()
@ -169,6 +178,7 @@ export const createDatabaseTreeNodes = (
children: [],
isSelected: () => useSelectedNode.getState().isDataNodeSelected(database.id()),
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(container, database.id()),
iconSrc: TreeDatabaseIcon,
onExpanded: async () => {
useSelectedNode.getState().setSelectedNode(database);
if (!databaseNode.children || databaseNode.children?.length === 0) {
@ -219,6 +229,7 @@ export const buildCollectionNode = (
children: children,
className: "collectionNode",
contextMenu: ResourceTreeContextMenuButtonFactory.createCollectionContextMenuButton(container, collection),
iconSrc: TreeCollectionIcon,
onClick: () => {
useSelectedNode.getState().setSelectedNode(collection);
collection.openTab();

View File

@ -52,7 +52,7 @@ export const isAccountRestrictedForConnectionStringLogin = async (connectionStri
const headers = new Headers();
headers.append(HttpHeaders.connectionString, connectionString);
const backendEndpoint: string = useNewPortalBackendEndpoint(BackendApi.PortalSettings)
const backendEndpoint: string = useNewPortalBackendEndpoint(BackendApi.AccountRestrictions)
? configContext.PORTAL_BACKEND_ENDPOINT
: configContext.BACKEND_ENDPOINT;

View File

@ -58,10 +58,10 @@ export const getRUThreshold = (): number => {
export const getDefaultQueryResultsView = (): SplitterDirection => {
const defaultQueryResultsViewRaw = LocalStorageUtility.getEntryString(StorageKey.DefaultQueryResultsView);
if (defaultQueryResultsViewRaw === SplitterDirection.Horizontal) {
return SplitterDirection.Horizontal;
if (defaultQueryResultsViewRaw === SplitterDirection.Vertical) {
return SplitterDirection.Vertical;
}
return SplitterDirection.Vertical;
return SplitterDirection.Horizontal;
};
export const DefaultRUThreshold = 5000;

View File

@ -52,7 +52,11 @@ export const defaultAllowedArmEndpoints: ReadonlyArray<string> = [
"https://management.chinacloudapi.cn",
];
export const allowedAadEndpoints: ReadonlyArray<string> = ["https://login.microsoftonline.com/"];
export const allowedAadEndpoints: ReadonlyArray<string> = [
"https://login.microsoftonline.com/",
"https://login.microsoftonline.us/",
"https://login.partner.microsoftonline.cn/",
];
export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
"https://main.documentdb.ext.azure.com",
@ -74,6 +78,13 @@ export const PortalBackendIPs: { [key: string]: string[] } = {
//usnat: ["7.28.202.68"],
};
export const PortalBackendOutboundIPs: { [key: string]: string[] } = {
[PortalBackendEndpoints.Mpac]: ["13.91.105.215", "4.210.172.107"],
[PortalBackendEndpoints.Prod]: ["13.88.56.148", "40.91.218.243"],
[PortalBackendEndpoints.Fairfax]: ["52.247.163.6", "52.244.134.181"],
[PortalBackendEndpoints.Mooncake]: ["163.228.137.6", "143.64.170.142"],
};
export const MongoProxyOutboundIPs: { [key: string]: string[] } = {
[MongoProxyEndpoints.Mpac]: ["20.245.81.54", "40.118.23.126"],
[MongoProxyEndpoints.Prod]: ["40.80.152.199", "13.95.130.121"],
@ -164,7 +175,23 @@ export function useNewPortalBackendEndpoint(backendApi: string): boolean {
PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod,
],
[BackendApi.AccountRestrictions]: [PortalBackendEndpoints.Development, PortalBackendEndpoints.Mpac],
[BackendApi.AccountRestrictions]: [
PortalBackendEndpoints.Development,
PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod,
],
[BackendApi.RuntimeProxy]: [
PortalBackendEndpoints.Development,
PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod,
],
[BackendApi.DisallowedLocations]: [
PortalBackendEndpoints.Development,
PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod,
PortalBackendEndpoints.Fairfax,
PortalBackendEndpoints.Mooncake,
],
};
if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) {

View File

@ -1,20 +1,15 @@
import { MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
import { resetConfigContext, updateConfigContext } from "ConfigContext";
import { DatabaseAccount, IpRule } from "Contracts/DataModels";
import { updateUserContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointUtils";
import { MongoProxyOutboundIPs, PortalBackendIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
describe("NetworkUtility tests", () => {
describe("getNetworkSettingsWarningMessage", () => {
const legacyBackendEndpoint: string = "https://main.documentdb.ext.azure.com";
const publicAccessMessagePart = "Please enable public access to proceed";
const accessMessagePart = "Please allow access from Azure Portal to proceed";
// validEnpoints are a subset of those from Utils/EndpointValidation/PortalBackendIPs
const validEndpoints = [
"https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn",
"https://main.documentdb.ext.azure.us",
];
let warningMessageResult: string;
const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
@ -52,52 +47,59 @@ describe("NetworkUtility tests", () => {
expect(warningMessageResult).toContain(publicAccessMessagePart);
});
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, () => {
validEndpoints.forEach(async (endpoint) => {
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
ipRules: PortalBackendIPs[endpoint].map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule),
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: endpoint,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toBeUndefined();
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, async () => {
const portalBackendOutboundIPsWithLegacyIPs: string[] = [
...PortalBackendOutboundIPs[PortalBackendEndpoints.Mpac],
...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
...MongoProxyOutboundIPs[MongoProxyEndpoints.Mpac],
...MongoProxyOutboundIPs[MongoProxyEndpoints.Prod],
...PortalBackendIPs["https://main.documentdb.ext.azure.com"],
];
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
ipRules: portalBackendOutboundIPsWithLegacyIPs.map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule),
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: legacyBackendEndpoint,
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toBeUndefined();
});
it("should return accessMessage when incorrent ip rule is added to mongo/cassandra account per endpoint", () => {
validEndpoints.forEach(async (endpoint) => {
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
ipRules: [{ ipAddressOrRange: "1.1.1.1" }],
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: endpoint,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toContain(accessMessagePart);
it("should return accessMessage when incorrent ip rule is added to mongo/cassandra account per endpoint", async () => {
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
ipRules: [{ ipAddressOrRange: "1.1.1.1" }],
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: legacyBackendEndpoint,
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toContain(accessMessagePart);
});
// Postgres and vcore mongo account checks basically pass through to CheckFirewallRules so those

View File

@ -1,7 +1,13 @@
import { CassandraProxyEndpoints, MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
import { configContext } from "ConfigContext";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import { userContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointUtils";
import {
CassandraProxyOutboundIPs,
MongoProxyOutboundIPs,
PortalBackendIPs,
PortalBackendOutboundIPs,
} from "Utils/EndpointUtils";
export const getNetworkSettingsWarningMessage = async (
setStateFunc: (warningMessage: string) => void,
@ -45,18 +51,53 @@ export const getNetworkSettingsWarningMessage = async (
const ipRules = accountProperties.ipRules;
// public network access is NOT set to "All networks"
if (ipRules?.length > 0) {
if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") {
const portalIPs = PortalBackendIPs[configContext.BACKEND_ENDPOINT];
let numberOfMatches = 0;
ipRules.forEach((ipRule) => {
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
numberOfMatches++;
}
});
const isProdOrMpacPortalBackendEndpoint: boolean = [
PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod,
].includes(configContext.PORTAL_BACKEND_ENDPOINT);
const portalBackendOutboundIPs: string[] = isProdOrMpacPortalBackendEndpoint
? [
...PortalBackendOutboundIPs[PortalBackendEndpoints.Mpac],
...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
]
: PortalBackendOutboundIPs[configContext.PORTAL_BACKEND_ENDPOINT];
let portalIPs: string[] = [...portalBackendOutboundIPs, ...PortalBackendIPs[configContext.BACKEND_ENDPOINT]];
if (numberOfMatches !== portalIPs.length) {
setStateFunc(accessMessage);
if (userContext.apiType === "Mongo") {
const isProdOrMpacMongoProxyEndpoint: boolean = [MongoProxyEndpoints.Mpac, MongoProxyEndpoints.Prod].includes(
configContext.MONGO_PROXY_ENDPOINT,
);
const mongoProxyOutboundIPs: string[] = isProdOrMpacMongoProxyEndpoint
? [...MongoProxyOutboundIPs[MongoProxyEndpoints.Mpac], ...MongoProxyOutboundIPs[MongoProxyEndpoints.Prod]]
: MongoProxyOutboundIPs[configContext.MONGO_PROXY_ENDPOINT];
portalIPs = [...portalIPs, ...mongoProxyOutboundIPs];
} else if (userContext.apiType === "Cassandra") {
const isProdOrMpacCassandraProxyEndpoint: boolean = [
CassandraProxyEndpoints.Mpac,
CassandraProxyEndpoints.Prod,
].includes(configContext.CASSANDRA_PROXY_ENDPOINT);
const cassandraProxyOutboundIPs: string[] = isProdOrMpacCassandraProxyEndpoint
? [
...CassandraProxyOutboundIPs[CassandraProxyEndpoints.Mpac],
...CassandraProxyOutboundIPs[CassandraProxyEndpoints.Prod],
]
: CassandraProxyOutboundIPs[configContext.CASSANDRA_PROXY_ENDPOINT];
portalIPs = [...portalIPs, ...cassandraProxyOutboundIPs];
}
let numberOfMatches = 0;
ipRules.forEach((ipRule) => {
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
numberOfMatches++;
}
});
if (numberOfMatches !== portalIPs.length) {
setStateFunc(accessMessage);
}
}
}

View File

@ -147,5 +147,33 @@ describe("Query Utils", () => {
expect(expectedPartitionKeyValues).toContain(documentContent["Type"]);
expect(expectedPartitionKeyValues).toContain(documentContent["Status"]);
});
it("should extract three partition key values even if one is empty", () => {
const multiPartitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.MultiHash,
paths: ["/Country", "/Region", "/Category"],
};
const expectedPartitionKeyValues: string[] = ["United States", "US-Washington", ""];
const partitioinKeyValues: PartitionKey[] = extractPartitionKeyValues(
documentContent,
multiPartitionKeyDefinition,
);
expect(partitioinKeyValues.length).toBe(3);
expect(expectedPartitionKeyValues).toContain(documentContent["Country"]);
expect(expectedPartitionKeyValues).toContain(documentContent["Region"]);
expect(expectedPartitionKeyValues).toContain(documentContent["Category"]);
});
it("should extract no partition key values in the case nested partition key", () => {
const singlePartitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.Hash,
paths: ["/Location.type"],
};
const partitionKeyValues: PartitionKey[] = extractPartitionKeyValues(
documentContent,
singlePartitionKeyDefinition,
);
expect(partitionKeyValues.length).toBe(0);
});
});
});

View File

@ -106,7 +106,7 @@ export const extractPartitionKeyValues = (
const partitionKeyValues: PartitionKey[] = [];
partitionKeyDefinition.paths.forEach((partitionKeyPath: string) => {
const partitionKeyPathWithoutSlash: string = partitionKeyPath.substring(1);
if (documentContent[partitionKeyPathWithoutSlash]) {
if (documentContent[partitionKeyPathWithoutSlash] !== undefined) {
partitionKeyValues.push(documentContent[partitionKeyPathWithoutSlash]);
}
});

View File

@ -619,6 +619,31 @@ function shouldForwardMessage(message: PortalMessage, messageOrigin: string) {
return messageOrigin === window.document.location.origin && message.type === MessageTypes.TelemetryInfo;
}
function updateAADEndpoints(portalEnv: PortalEnv) {
switch (portalEnv) {
case "prod1":
case "prod":
updateConfigContext({
AAD_ENDPOINT: Constants.AadEndpoints.Prod,
});
break;
case "fairfax":
updateConfigContext({
AAD_ENDPOINT: Constants.AadEndpoints.Fairfax,
});
break;
case "mooncake":
updateConfigContext({
AAD_ENDPOINT: Constants.AadEndpoints.Mooncake,
});
break;
default:
console.warn(`Unknown portal environment: ${portalEnv}`);
break;
}
}
function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
if (
configContext.BACKEND_ENDPOINT &&
@ -639,6 +664,8 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
PORTAL_BACKEND_ENDPOINT: inputs.portalBackendEndpoint,
});
updateAADEndpoints(inputs.serverId as PortalEnv);
updateUserContext({
authorizationToken,
databaseAccount,

View File

@ -1,14 +1,34 @@
import { useEffect, useState } from "react";
import { ApiEndpoints } from "../Common/Constants";
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
import { ApiEndpoints, BackendApi, HttpHeaders } from "../Common/Constants";
import { configContext } from "../ConfigContext";
import { AccessInputMetadata } from "../Contracts/DataModels";
const url = `${configContext.BACKEND_ENDPOINT}${ApiEndpoints.guestRuntimeProxy}/accessinputmetadata?_=1609359229955`;
export async function fetchAccessData(portalToken: string): Promise<AccessInputMetadata> {
if (!useNewPortalBackendEndpoint(BackendApi.RuntimeProxy)) {
return fetchAccessData_ToBeDeprecated(portalToken);
}
const headers = new Headers();
// Portal encrypted token API quirk: The token header must be URL encoded
headers.append("x-ms-encrypted-auth-token", encodeURIComponent(portalToken));
headers.append(HttpHeaders.guestAccessToken, encodeURIComponent(portalToken));
const url: string = `${configContext.PORTAL_BACKEND_ENDPOINT}/api/connectionstring/runtimeproxy/accessinputmetadata`;
const options = {
method: "GET",
headers: headers,
};
return fetch(url, options)
.then((response) => response.json())
.catch((error) => console.error(error));
}
export async function fetchAccessData_ToBeDeprecated(portalToken: string): Promise<AccessInputMetadata> {
const headers = new Headers();
// Portal encrypted token API quirk: The token header must be URL encoded
headers.append(HttpHeaders.guestAccessToken, encodeURIComponent(portalToken));
const options = {
method: "GET",