mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-06 03:00:23 +00:00
Compare commits
9 Commits
users/nish
...
sung_test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ae92f2398 | ||
|
|
a0853c9167 | ||
|
|
2fdb3df4ae | ||
|
|
7c9802c07d | ||
|
|
e5609bd91e | ||
|
|
4b75e86b74 | ||
|
|
abf061089d | ||
|
|
ec25586a6e | ||
|
|
c15d1432b2 |
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
@@ -113,7 +113,7 @@ jobs:
|
||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||
AZURE_DEVOPS_PAT: ${{ secrets.AZURE_DEVOPS_PAT }}
|
||||
steps:
|
||||
- uses: nuget/setup-nuget@v1
|
||||
- uses: nuget/setup-nuget@v2
|
||||
with:
|
||||
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
|
||||
- name: Download Dist Folder
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||
AZURE_DEVOPS_PAT: ${{ secrets.AZURE_DEVOPS_PAT }}
|
||||
steps:
|
||||
- uses: nuget/setup-nuget@v1
|
||||
- uses: nuget/setup-nuget@v2
|
||||
with:
|
||||
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
|
||||
- name: Download Dist Folder
|
||||
@@ -185,9 +185,9 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: blob-report-${{ matrix.shardIndex }}
|
||||
path: blob-report
|
||||
retention-days: 1
|
||||
name: blob-report-${{ matrix.shardIndex }}
|
||||
path: blob-report
|
||||
retention-days: 1
|
||||
|
||||
merge-playwright-reports:
|
||||
name: "Merge Playwright Reports"
|
||||
@@ -197,26 +197,26 @@ jobs:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: all-blob-reports
|
||||
pattern: blob-report-*
|
||||
merge-multiple: true
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: all-blob-reports
|
||||
pattern: blob-report-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge into HTML Report
|
||||
run: npx playwright merge-reports --reporter html ./all-blob-reports
|
||||
- name: Merge into HTML Report
|
||||
run: npx playwright merge-reports --reporter html ./all-blob-reports
|
||||
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: html-report--attempt-${{ github.run_attempt }}
|
||||
path: playwright-report
|
||||
retention-days: 14
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: html-report--attempt-${{ github.run_attempt }}
|
||||
path: playwright-report
|
||||
retention-days: 14
|
||||
|
||||
101
package-lock.json
generated
101
package-lock.json
generated
@@ -122,7 +122,7 @@
|
||||
"@babel/preset-env": "7.24.7",
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@babel/preset-typescript": "7.24.7",
|
||||
"@playwright/test": "1.44.0",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@types/applicationinsights-js": "1.0.7",
|
||||
"@types/codemirror": "0.0.56",
|
||||
@@ -10175,34 +10175,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.44.0",
|
||||
"version": "1.49.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz",
|
||||
"integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.44.0"
|
||||
"playwright": "1.49.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test/node_modules/playwright": {
|
||||
"version": "1.44.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.44.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
@@ -14804,6 +14788,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bintrees": {
|
||||
"version": "1.0.2",
|
||||
"license": "MIT"
|
||||
@@ -19466,6 +19459,12 @@
|
||||
"version": "2.0.5",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/filesize": {
|
||||
"version": "8.0.7",
|
||||
"dev": true,
|
||||
@@ -20054,6 +20053,19 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"license": "MIT",
|
||||
@@ -24160,6 +24172,24 @@
|
||||
"fsevents": "^1.2.7"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-haste-map/node_modules/fsevents": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"deprecated": "Upgrade to fsevents v2 to mitigate potential security issues",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"dependencies": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-haste-map/node_modules/jest-worker": {
|
||||
"version": "24.9.0",
|
||||
"license": "MIT",
|
||||
@@ -30798,15 +30828,34 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.44.0",
|
||||
"node_modules/playwright": {
|
||||
"version": "1.49.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz",
|
||||
"integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.49.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
|
||||
"integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/plotly.js-cartesian-dist-min": {
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
"@babel/preset-env": "7.24.7",
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@babel/preset-typescript": "7.24.7",
|
||||
"@playwright/test": "1.44.0",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@types/applicationinsights-js": "1.0.7",
|
||||
"@types/codemirror": "0.0.56",
|
||||
@@ -170,10 +170,10 @@
|
||||
"jest": "29.7.0",
|
||||
"jest-canvas-mock": "2.5.2",
|
||||
"jest-circus": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"jest-html-loader": "1.0.0",
|
||||
"jest-react-hooks-shallow": "1.5.1",
|
||||
"jest-trx-results-processor": "3.0.2",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"less": "3.8.1",
|
||||
"less-loader": "11.1.3",
|
||||
"less-vars-loader": "1.1.0",
|
||||
|
||||
@@ -149,7 +149,7 @@ export class PortalBackendEndpoints {
|
||||
}
|
||||
|
||||
export class MongoProxyEndpoints {
|
||||
public static readonly Local: string = "https://localhost:7238";
|
||||
public static readonly Development: string = "https://localhost:7238";
|
||||
public static readonly Mpac: string = "https://cdb-ms-mpac-mp.cosmos.azure.com";
|
||||
public static readonly Prod: string = "https://cdb-ms-prod-mp.cosmos.azure.com";
|
||||
public static readonly Fairfax: string = "https://cdb-ff-prod-mp.cosmos.azure.us";
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AuthType } from "../AuthType";
|
||||
import { BackendApi, PriorityLevel } from "../Common/Constants";
|
||||
import * as Logger from "../Common/Logger";
|
||||
import { Platform, configContext } from "../ConfigContext";
|
||||
import { userContext } from "../UserContext";
|
||||
import { updateUserContext, userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||
@@ -189,10 +189,19 @@ let _client: Cosmos.CosmosClient;
|
||||
|
||||
export function client(): Cosmos.CosmosClient {
|
||||
if (_client) {
|
||||
if (!userContext.hasDataPlaneRbacSettingChanged) {
|
||||
if (!userContext.refreshCosmosClient) {
|
||||
return _client;
|
||||
}
|
||||
_client.dispose();
|
||||
_client = null;
|
||||
}
|
||||
|
||||
if (userContext.refreshCosmosClient) {
|
||||
updateUserContext({
|
||||
refreshCosmosClient: false,
|
||||
});
|
||||
}
|
||||
|
||||
let _defaultHeaders: Cosmos.CosmosHeaders = {};
|
||||
_defaultHeaders["x-ms-cosmos-sdk-supportedcapabilities"] =
|
||||
SDKSupportedCapabilities.None | SDKSupportedCapabilities.PartitionMerge;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { QueryOperationOptions } from "@azure/cosmos";
|
||||
// import { QueryOperationOptions } from "@azure/cosmos";
|
||||
import { QueryResults } from "../Contracts/ViewModels";
|
||||
|
||||
interface QueryResponse {
|
||||
@@ -11,17 +11,13 @@ interface QueryResponse {
|
||||
}
|
||||
|
||||
export interface MinimalQueryIterator {
|
||||
fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>;
|
||||
fetchNext: () => Promise<QueryResponse>;
|
||||
}
|
||||
|
||||
// Pick<QueryIterator<any>, "fetchNext">;
|
||||
|
||||
export function nextPage(
|
||||
documentsIterator: MinimalQueryIterator,
|
||||
firstItemIndex: number,
|
||||
queryOperationOptions?: QueryOperationOptions,
|
||||
): Promise<QueryResults> {
|
||||
return documentsIterator.fetchNext(queryOperationOptions).then((response) => {
|
||||
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
||||
return documentsIterator.fetchNext().then((response) => {
|
||||
const documents = response.resources;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
||||
|
||||
@@ -722,63 +722,63 @@ export function getEndpoint(endpoint: string): string {
|
||||
export function useMongoProxyEndpoint(mongoProxyApi: string): boolean {
|
||||
const mongoProxyEnvironmentMap: { [key: string]: string[] } = {
|
||||
[MongoProxyApi.ResourceList]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.QueryDocuments]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.CreateDocument]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.ReadDocument]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.UpdateDocument]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.DeleteDocument]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.CreateCollectionWithProxy]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.LegacyMongoShell]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
MongoProxyEndpoints.Mooncake,
|
||||
],
|
||||
[MongoProxyApi.BulkDelete]: [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { QueryOperationOptions } from "@azure/cosmos";
|
||||
// import { QueryOperationOptions } from "@azure/cosmos";
|
||||
import { QueryResults } from "../../Contracts/ViewModels";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
@@ -9,13 +9,13 @@ export const queryDocumentsPage = async (
|
||||
resourceName: string,
|
||||
documentsIterator: MinimalQueryIterator,
|
||||
firstItemIndex: number,
|
||||
queryOperationOptions?: QueryOperationOptions,
|
||||
// queryOperationOptions?: QueryOperationOptions,
|
||||
): Promise<QueryResults> => {
|
||||
const entityName = getEntityName();
|
||||
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
|
||||
|
||||
try {
|
||||
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex, queryOperationOptions);
|
||||
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
|
||||
const itemCount = (result.documents && result.documents.length) || 0;
|
||||
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
|
||||
return result;
|
||||
|
||||
@@ -819,22 +819,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
|
||||
{this.shouldShowAnalyticalStoreOptions() && (
|
||||
<Stack className="panelGroupSpacing">
|
||||
<Stack horizontal>
|
||||
<Text className="panelTextBold" variant="small">
|
||||
Analytical store
|
||||
</Text>
|
||||
<TooltipHost
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={this.getAnalyticalStorageTooltipContent()}
|
||||
>
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads."
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
<Text className="panelTextBold" variant="small">
|
||||
{this.getAnalyticalStorageContent()}
|
||||
</Text>
|
||||
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<div role="radiogroup">
|
||||
@@ -1230,7 +1217,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
return "";
|
||||
}
|
||||
|
||||
private getAnalyticalStorageTooltipContent(): JSX.Element {
|
||||
private getAnalyticalStorageContent(): JSX.Element {
|
||||
return (
|
||||
<Text variant="small">
|
||||
Enable analytical store capability to perform near real-time analytics on your operational data, without
|
||||
|
||||
@@ -193,6 +193,17 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
|
||||
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
|
||||
|
||||
if (
|
||||
enableDataPlaneRBACOption !== LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) ||
|
||||
retryAttempts !== LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts) ||
|
||||
retryInterval !== LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval) ||
|
||||
MaxWaitTimeInSeconds !== LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds)
|
||||
) {
|
||||
updateUserContext({
|
||||
refreshCosmosClient: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (configContext.platform !== Platform.Fabric) {
|
||||
LocalStorageUtility.setEntryString(StorageKey.DataPlaneRbacEnabled, enableDataPlaneRBACOption);
|
||||
if (
|
||||
@@ -202,7 +213,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
) {
|
||||
updateUserContext({
|
||||
dataPlaneRbacEnabled: true,
|
||||
hasDataPlaneRbacSettingChanged: true,
|
||||
});
|
||||
useDataPlaneRbac.setState({ dataPlaneRbacEnabled: true });
|
||||
try {
|
||||
@@ -226,7 +236,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
} else {
|
||||
updateUserContext({
|
||||
dataPlaneRbacEnabled: false,
|
||||
hasDataPlaneRbacSettingChanged: true,
|
||||
});
|
||||
const { databaseAccount: account, subscriptionId, resourceGroup } = userContext;
|
||||
if (!userContext.features.enableAadDataPlane && !userContext.masterKey) {
|
||||
@@ -564,7 +573,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" && (
|
||||
<>
|
||||
<AccordionItem value="3">
|
||||
@@ -663,78 +671,79 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</AccordionItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
<AccordionItem value="6">
|
||||
<AccordionHeader>
|
||||
<div className={styles.header}>Retry Settings</div>
|
||||
</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<div className={styles.settingsSectionContainer}>
|
||||
<div className={styles.settingsSectionDescription}>
|
||||
Retry policy associated with throttled requests during CosmosDB queries.
|
||||
{(userContext.apiType === "SQL" || userContext.apiType === "Tables" || userContext.apiType === "Gremlin") && (
|
||||
<AccordionItem value="6">
|
||||
<AccordionHeader>
|
||||
<div className={styles.header}>Retry Settings</div>
|
||||
</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<div className={styles.settingsSectionContainer}>
|
||||
<div className={styles.settingsSectionDescription}>
|
||||
Retry policy associated with throttled requests during CosmosDB queries.
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.subHeader}>Max retry attempts</span>
|
||||
<InfoTooltip className={styles.headerIcon}>
|
||||
Max number of retries to be performed for a request. Default value 9.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<SpinButton
|
||||
labelPosition={Position.top}
|
||||
min={1}
|
||||
step={1}
|
||||
value={"" + retryAttempts}
|
||||
onChange={handleOnQueryRetryAttemptsSpinButtonChange}
|
||||
incrementButtonAriaLabel="Increase value by 1"
|
||||
decrementButtonAriaLabel="Decrease value by 1"
|
||||
onIncrement={(newValue) => setRetryAttempts(parseInt(newValue) + 1 || retryAttempts)}
|
||||
onDecrement={(newValue) => setRetryAttempts(parseInt(newValue) - 1 || retryAttempts)}
|
||||
onValidate={(newValue) => setRetryAttempts(parseInt(newValue) || retryAttempts)}
|
||||
styles={spinButtonStyles}
|
||||
/>
|
||||
<div>
|
||||
<span className={styles.subHeader}>Fixed retry interval (ms)</span>
|
||||
<InfoTooltip className={styles.headerIcon}>
|
||||
Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned
|
||||
as part of the response. Default value is 0 milliseconds.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<SpinButton
|
||||
labelPosition={Position.top}
|
||||
min={1000}
|
||||
step={1000}
|
||||
value={"" + retryInterval}
|
||||
onChange={handleOnRetryIntervalSpinButtonChange}
|
||||
incrementButtonAriaLabel="Increase value by 1000"
|
||||
decrementButtonAriaLabel="Decrease value by 1000"
|
||||
onIncrement={(newValue) => setRetryInterval(parseInt(newValue) + 1000 || retryInterval)}
|
||||
onDecrement={(newValue) => setRetryInterval(parseInt(newValue) - 1000 || retryInterval)}
|
||||
onValidate={(newValue) => setRetryInterval(parseInt(newValue) || retryInterval)}
|
||||
styles={spinButtonStyles}
|
||||
/>
|
||||
<div>
|
||||
<span className={styles.subHeader}>Max wait time (s)</span>
|
||||
<InfoTooltip className={styles.headerIcon}>
|
||||
Max wait time in seconds to wait for a request while the retries are happening. Default value 30
|
||||
seconds.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<SpinButton
|
||||
labelPosition={Position.top}
|
||||
min={1}
|
||||
step={1}
|
||||
value={"" + MaxWaitTimeInSeconds}
|
||||
onChange={handleOnMaxWaitTimeSpinButtonChange}
|
||||
incrementButtonAriaLabel="Increase value by 1"
|
||||
decrementButtonAriaLabel="Decrease value by 1"
|
||||
onIncrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) + 1 || MaxWaitTimeInSeconds)}
|
||||
onDecrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) - 1 || MaxWaitTimeInSeconds)}
|
||||
onValidate={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) || MaxWaitTimeInSeconds)}
|
||||
styles={spinButtonStyles}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.subHeader}>Max retry attempts</span>
|
||||
<InfoTooltip className={styles.headerIcon}>
|
||||
Max number of retries to be performed for a request. Default value 9.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<SpinButton
|
||||
labelPosition={Position.top}
|
||||
min={1}
|
||||
step={1}
|
||||
value={"" + retryAttempts}
|
||||
onChange={handleOnQueryRetryAttemptsSpinButtonChange}
|
||||
incrementButtonAriaLabel="Increase value by 1"
|
||||
decrementButtonAriaLabel="Decrease value by 1"
|
||||
onIncrement={(newValue) => setRetryAttempts(parseInt(newValue) + 1 || retryAttempts)}
|
||||
onDecrement={(newValue) => setRetryAttempts(parseInt(newValue) - 1 || retryAttempts)}
|
||||
onValidate={(newValue) => setRetryAttempts(parseInt(newValue) || retryAttempts)}
|
||||
styles={spinButtonStyles}
|
||||
/>
|
||||
<div>
|
||||
<span className={styles.subHeader}>Fixed retry interval (ms)</span>
|
||||
<InfoTooltip className={styles.headerIcon}>
|
||||
Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned as
|
||||
part of the response. Default value is 0 milliseconds.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<SpinButton
|
||||
labelPosition={Position.top}
|
||||
min={1000}
|
||||
step={1000}
|
||||
value={"" + retryInterval}
|
||||
onChange={handleOnRetryIntervalSpinButtonChange}
|
||||
incrementButtonAriaLabel="Increase value by 1000"
|
||||
decrementButtonAriaLabel="Decrease value by 1000"
|
||||
onIncrement={(newValue) => setRetryInterval(parseInt(newValue) + 1000 || retryInterval)}
|
||||
onDecrement={(newValue) => setRetryInterval(parseInt(newValue) - 1000 || retryInterval)}
|
||||
onValidate={(newValue) => setRetryInterval(parseInt(newValue) || retryInterval)}
|
||||
styles={spinButtonStyles}
|
||||
/>
|
||||
<div>
|
||||
<span className={styles.subHeader}>Max wait time (s)</span>
|
||||
<InfoTooltip className={styles.headerIcon}>
|
||||
Max wait time in seconds to wait for a request while the retries are happening. Default value 30
|
||||
seconds.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<SpinButton
|
||||
labelPosition={Position.top}
|
||||
min={1}
|
||||
step={1}
|
||||
value={"" + MaxWaitTimeInSeconds}
|
||||
onChange={handleOnMaxWaitTimeSpinButtonChange}
|
||||
incrementButtonAriaLabel="Increase value by 1"
|
||||
decrementButtonAriaLabel="Decrease value by 1"
|
||||
onIncrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) + 1 || MaxWaitTimeInSeconds)}
|
||||
onDecrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) - 1 || MaxWaitTimeInSeconds)}
|
||||
onValidate={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) || MaxWaitTimeInSeconds)}
|
||||
styles={spinButtonStyles}
|
||||
/>
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
|
||||
<AccordionItem value="7">
|
||||
<AccordionHeader>
|
||||
@@ -758,7 +767,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
||||
{shouldShowCrossPartitionOption && (
|
||||
<AccordionItem value="8">
|
||||
<AccordionHeader>
|
||||
@@ -784,7 +792,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
|
||||
{shouldShowParallelismOption && (
|
||||
<AccordionItem value="9">
|
||||
<AccordionHeader>
|
||||
@@ -818,7 +825,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
|
||||
{shouldShowPriorityLevelOption && (
|
||||
<AccordionItem value="10">
|
||||
<AccordionHeader>
|
||||
@@ -842,7 +848,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
|
||||
{shouldShowGraphAutoVizOption && (
|
||||
<AccordionItem value="11">
|
||||
<AccordionHeader>
|
||||
@@ -864,7 +869,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
|
||||
{shouldShowCopilotSampleDBOption && (
|
||||
<AccordionItem value="12">
|
||||
<AccordionHeader>
|
||||
|
||||
@@ -309,40 +309,23 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
||||
<Stack
|
||||
className="panelGroupSpacing"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
<Text
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Analytical store
|
||||
Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/analytical-store-overview"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content={
|
||||
<Text
|
||||
variant="small"
|
||||
>
|
||||
Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.
|
||||
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/analytical-store-overview"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
}
|
||||
directionalHint={4}
|
||||
>
|
||||
<Icon
|
||||
ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads."
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</StyledTooltipHostBase>
|
||||
</Stack>
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable no-console */
|
||||
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos";
|
||||
import { FeedOptions } from "@azure/cosmos";
|
||||
import QueryError, { createMonacoErrorLocationResolver, createMonacoMarkersForQueryErrors } from "Common/QueryError";
|
||||
import { SplitterDirection } from "Common/Splitter";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
@@ -18,7 +18,7 @@ import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey, getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { Allotment } from "allotment";
|
||||
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
@@ -368,19 +368,19 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
isExecutionError: false,
|
||||
});
|
||||
|
||||
let queryOperationOptions: QueryOperationOptions;
|
||||
if (userContext.apiType === "SQL" && ruThresholdEnabled()) {
|
||||
const ruThreshold: number = getRUThreshold();
|
||||
queryOperationOptions = {
|
||||
ruCapPerOperation: ruThreshold,
|
||||
} as QueryOperationOptions;
|
||||
}
|
||||
// let queryOperationOptions: QueryOperationOptions;
|
||||
// if (userContext.apiType === "SQL" && ruThresholdEnabled()) {
|
||||
// const ruThreshold: number = getRUThreshold();
|
||||
// queryOperationOptions = {
|
||||
// ruCapPerOperation: ruThreshold,
|
||||
// } as QueryOperationOptions;
|
||||
// }
|
||||
const queryDocuments = async (firstItemIndex: number) =>
|
||||
await queryDocumentsPage(
|
||||
this.props.collection && this.props.collection.id(),
|
||||
this._iterator,
|
||||
firstItemIndex,
|
||||
queryOperationOptions,
|
||||
// queryOperationOptions,
|
||||
);
|
||||
this.props.tabsBaseInstance.isExecuting(true);
|
||||
this.setState({
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { IMessageBarStyles, MessageBar, MessageBarButton, MessageBarType } from "@fluentui/react";
|
||||
import { IMessageBarStyles, MessageBar, MessageBarType } from "@fluentui/react";
|
||||
import { CassandraProxyEndpoints, MongoProxyEndpoints } from "Common/Constants";
|
||||
import { sendMessage } from "Common/MessageHandler";
|
||||
import { configContext } from "ConfigContext";
|
||||
import { IpRule } from "Contracts/DataModels";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { CollectionTabKind } from "Contracts/ViewModels";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||
@@ -35,7 +33,7 @@ interface TabsProps {
|
||||
}
|
||||
|
||||
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||
const { openedTabs, openedReactTabs, activeTab, activeReactTab, networkSettingsWarning } = useTabs();
|
||||
const { openedTabs, openedReactTabs, activeTab, activeReactTab } = useTabs();
|
||||
const [
|
||||
showMongoAndCassandraProxiesNetworkSettingsWarningState,
|
||||
setShowMongoAndCassandraProxiesNetworkSettingsWarningState,
|
||||
@@ -60,29 +58,6 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||
|
||||
return (
|
||||
<div className="tabsManagerContainer">
|
||||
{networkSettingsWarning && (
|
||||
<MessageBar
|
||||
messageBarType={MessageBarType.warning}
|
||||
styles={defaultMessageBarStyles}
|
||||
actions={
|
||||
<MessageBarButton
|
||||
onClick={() =>
|
||||
sendMessage({
|
||||
type:
|
||||
userContext.apiType === "VCoreMongo"
|
||||
? MessageTypes.OpenVCoreMongoNetworkingBlade
|
||||
: MessageTypes.OpenPostgresNetworkingBlade,
|
||||
})
|
||||
}
|
||||
>
|
||||
Change network settings
|
||||
</MessageBarButton>
|
||||
}
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
>
|
||||
{networkSettingsWarning}
|
||||
</MessageBar>
|
||||
)}
|
||||
{showMongoAndCassandraProxiesNetworkSettingsWarningState && (
|
||||
<MessageBar
|
||||
messageBarType={MessageBarType.warning}
|
||||
@@ -343,7 +318,7 @@ const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): J
|
||||
const showMongoAndCassandraProxiesNetworkSettingsWarning = (): boolean => {
|
||||
const ipRules: IpRule[] = userContext.databaseAccount?.properties?.ipRules;
|
||||
if (
|
||||
((userContext.apiType === "Mongo" && configContext.MONGO_PROXY_ENDPOINT !== MongoProxyEndpoints.Local) ||
|
||||
((userContext.apiType === "Mongo" && configContext.MONGO_PROXY_ENDPOINT !== MongoProxyEndpoints.Development) ||
|
||||
(userContext.apiType === "Cassandra" &&
|
||||
configContext.CASSANDRA_PROXY_ENDPOINT !== CassandraProxyEndpoints.Development)) &&
|
||||
ipRules?.length
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { initializeIcons, Link, Text } from "@fluentui/react";
|
||||
import { Link, Text } from "@fluentui/react";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
@@ -20,7 +20,7 @@ const createAccountUrl = "https://aka.ms/cosmos-create-account-portal";
|
||||
const onInit = async () => {
|
||||
const dataExplorerUrl = new URL("./", window.location.href).href;
|
||||
|
||||
initializeIcons();
|
||||
// initializeIcons();
|
||||
await initializeConfiguration();
|
||||
const galleryViewerProps = GalleryUtils.getGalleryViewerProps(window.location.search);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { initializeIcons } from "@fluentui/react";
|
||||
// import { initializeIcons } from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { AadAuthorizationFailure } from "Platform/Hosted/Components/AadAuthorizationFailure";
|
||||
import * as React from "react";
|
||||
@@ -22,7 +22,7 @@ import { useAADAuth } from "./hooks/useAADAuth";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useTokenMetadata } from "./hooks/usePortalAccessToken";
|
||||
|
||||
initializeIcons();
|
||||
// initializeIcons();
|
||||
|
||||
const App: React.FunctionComponent = () => {
|
||||
// For handling encrypted portal tokens sent via query paramter
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import "./ReactDevTools";
|
||||
|
||||
// CSS Dependencies
|
||||
import { initializeIcons, loadTheme } from "@fluentui/react";
|
||||
import { loadTheme } from "@fluentui/react";
|
||||
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
|
||||
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
|
||||
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
|
||||
@@ -62,7 +62,7 @@ import "./Shared/appInsights";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||
|
||||
initializeIcons();
|
||||
// initializeIcons();
|
||||
|
||||
const App: React.FunctionComponent = () => {
|
||||
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { initializeIcons } from "@fluentui/react";
|
||||
// import { initializeIcons } from "@fluentui/react";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
@@ -14,7 +14,7 @@ import { IGalleryItem, JunoClient } from "../Juno/JunoClient";
|
||||
import * as GalleryUtils from "../Utils/GalleryUtils";
|
||||
|
||||
const onInit = async () => {
|
||||
initializeIcons();
|
||||
// initializeIcons();
|
||||
await initializeConfiguration();
|
||||
const galleryViewerProps = GalleryUtils.getGalleryViewerProps(window.location.search);
|
||||
const notebookViewerProps = GalleryUtils.getNotebookViewerProps(window.location.search);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { initializeIcons, Spinner, SpinnerSize } from "@fluentui/react";
|
||||
import { Spinner, SpinnerSize } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { withTranslation } from "react-i18next";
|
||||
@@ -13,7 +13,7 @@ import "./SelfServe.less";
|
||||
import { SelfServeComponent } from "./SelfServeComponent";
|
||||
import { SelfServeDescriptor } from "./SelfServeTypes";
|
||||
import { SelfServeType } from "./SelfServeUtils";
|
||||
initializeIcons();
|
||||
// initializeIcons();
|
||||
|
||||
const loadTranslationFile = async (className: string): Promise<void> => {
|
||||
const language = i18n.languages[0];
|
||||
|
||||
@@ -266,7 +266,10 @@ export const getOfferingIds = async (regions: Array<RegionItem>): Promise<Offeri
|
||||
method: "GET",
|
||||
apiVersion: "2023-05-01-preview",
|
||||
queryParams: {
|
||||
filter: "armRegionName eq '" + regionShortName + "'",
|
||||
filter:
|
||||
"armRegionName eq '" +
|
||||
regionShortName +
|
||||
"' and productDisplayName eq 'Azure Cosmos DB Dedicated Gateway - General Purpose'",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ export interface UserContext {
|
||||
readonly vcoreMongoConnectionParams?: VCoreMongoConnectionParams;
|
||||
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
|
||||
readonly dataPlaneRbacEnabled?: boolean;
|
||||
readonly hasDataPlaneRbacSettingChanged?: boolean;
|
||||
readonly refreshCosmosClient?: boolean;
|
||||
}
|
||||
|
||||
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";
|
||||
|
||||
@@ -93,7 +93,7 @@ export const MongoProxyOutboundIPs: { [key: string]: string[] } = {
|
||||
};
|
||||
|
||||
export const defaultAllowedMongoProxyEndpoints: ReadonlyArray<string> = [
|
||||
MongoProxyEndpoints.Local,
|
||||
MongoProxyEndpoints.Development,
|
||||
MongoProxyEndpoints.Mpac,
|
||||
MongoProxyEndpoints.Prod,
|
||||
MongoProxyEndpoints.Fairfax,
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import { MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
|
||||
import { resetConfigContext, updateConfigContext } from "ConfigContext";
|
||||
import { DatabaseAccount, IpRule } from "Contracts/DataModels";
|
||||
import { updateUserContext } from "UserContext";
|
||||
import { MongoProxyOutboundIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
|
||||
import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
|
||||
|
||||
describe("NetworkUtility tests", () => {
|
||||
describe("getNetworkSettingsWarningMessage", () => {
|
||||
const publicAccessMessagePart = "Please enable public access to proceed";
|
||||
const accessMessagePart = "Please allow access from Azure Portal to proceed";
|
||||
let warningMessageResult: string;
|
||||
const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
|
||||
|
||||
beforeEach(() => {
|
||||
warningMessageResult = undefined;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetConfigContext();
|
||||
});
|
||||
|
||||
it("should return no message when publicNetworkAccess is enabled", async () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
publicNetworkAccess: "Enabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
await getNetworkSettingsWarningMessage(warningMessageFunc);
|
||||
expect(warningMessageResult).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return publicAccessMessage when publicNetworkAccess is disabled", async () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
publicNetworkAccess: "Disabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
await getNetworkSettingsWarningMessage(warningMessageFunc);
|
||||
expect(warningMessageResult).toContain(publicAccessMessagePart);
|
||||
});
|
||||
|
||||
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, async () => {
|
||||
const portalBackendOutboundIPs: string[] = [
|
||||
...PortalBackendOutboundIPs[PortalBackendEndpoints.Mpac],
|
||||
...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
|
||||
...MongoProxyOutboundIPs[MongoProxyEndpoints.Mpac],
|
||||
...MongoProxyOutboundIPs[MongoProxyEndpoints.Prod],
|
||||
];
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
kind: "MongoDB",
|
||||
properties: {
|
||||
ipRules: portalBackendOutboundIPs.map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule),
|
||||
publicNetworkAccess: "Enabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
updateConfigContext({
|
||||
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", async () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
kind: "MongoDB",
|
||||
properties: {
|
||||
ipRules: [{ ipAddressOrRange: "1.1.1.1" }],
|
||||
publicNetworkAccess: "Enabled",
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
updateConfigContext({
|
||||
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
|
||||
// tests are omitted here and included in CheckFirewallRules.test.ts
|
||||
});
|
||||
});
|
||||
@@ -1,99 +0,0 @@
|
||||
import { CassandraProxyEndpoints, MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
|
||||
import { configContext } from "ConfigContext";
|
||||
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
|
||||
import { userContext } from "UserContext";
|
||||
import { CassandraProxyOutboundIPs, MongoProxyOutboundIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
|
||||
|
||||
export const getNetworkSettingsWarningMessage = async (
|
||||
setStateFunc: (warningMessage: string) => void,
|
||||
): Promise<void> => {
|
||||
const accountProperties = userContext.databaseAccount?.properties;
|
||||
const accessMessage =
|
||||
"The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure Portal to proceed.";
|
||||
const publicAccessMessage =
|
||||
"The Network settings for this account are preventing access from Data Explorer. Please enable public access to proceed.";
|
||||
|
||||
if (userContext.apiType === "Postgres") {
|
||||
checkFirewallRules(
|
||||
"2022-11-08",
|
||||
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255",
|
||||
undefined,
|
||||
setStateFunc,
|
||||
accessMessage,
|
||||
);
|
||||
return;
|
||||
} else if (userContext.apiType === "VCoreMongo") {
|
||||
checkFirewallRules(
|
||||
"2023-03-01-preview",
|
||||
(rule) =>
|
||||
rule.name.startsWith("AllowAllAzureServicesAndResourcesWithinAzureIps") ||
|
||||
(rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"),
|
||||
undefined,
|
||||
setStateFunc,
|
||||
accessMessage,
|
||||
);
|
||||
return;
|
||||
} else if (accountProperties) {
|
||||
// public network access is disabled
|
||||
if (
|
||||
accountProperties.publicNetworkAccess !== "Enabled" &&
|
||||
accountProperties.publicNetworkAccess !== "SecuredByPerimeter"
|
||||
) {
|
||||
setStateFunc(publicAccessMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const ipRules = accountProperties.ipRules;
|
||||
// public network access is NOT set to "All networks"
|
||||
if (ipRules?.length > 0) {
|
||||
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];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
} from "Shared/AppStatePersistenceUtility";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
|
||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
@@ -142,7 +141,7 @@ async function configureFabric(): Promise<Explorer> {
|
||||
await scheduleRefreshDatabaseResourceToken(true);
|
||||
resolve(explorer);
|
||||
await explorer.refreshAllDatabases();
|
||||
if (userContext.fabricContext.isVisible && !firstContainerOpened) {
|
||||
if (userContext.fabricContext.isVisible) {
|
||||
firstContainerOpened = true;
|
||||
openFirstContainer(explorer, userContext.fabricContext.databaseConnectionInfo.databaseId);
|
||||
}
|
||||
@@ -439,6 +438,7 @@ function createExplorerFabric(params: { connectionId: string; isVisible: boolean
|
||||
},
|
||||
},
|
||||
});
|
||||
useTabs.getState().closeAllTabs();
|
||||
const explorer = new Explorer();
|
||||
return explorer;
|
||||
}
|
||||
@@ -741,8 +741,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkSettingsWarningMessage(useTabs.getState().setNetworkSettingsWarning);
|
||||
|
||||
if (inputs.features) {
|
||||
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ export interface TabsState {
|
||||
openedReactTabs: ReactTabKind[];
|
||||
activeTab: TabsBase | undefined;
|
||||
activeReactTab: ReactTabKind | undefined;
|
||||
networkSettingsWarning: string;
|
||||
queryCopilotTabInitialInput: string;
|
||||
isTabExecuting: boolean;
|
||||
isQueryErrorThrown: boolean;
|
||||
@@ -33,7 +32,6 @@ export interface TabsState {
|
||||
closeAllNotebookTabs: (hardClose: boolean) => void;
|
||||
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
||||
closeReactTab: (tabKind: ReactTabKind) => void;
|
||||
setNetworkSettingsWarning: (warningMessage: string) => void;
|
||||
setQueryCopilotTabInitialInput: (input: string) => void;
|
||||
setIsTabExecuting: (state: boolean) => void;
|
||||
setIsQueryErrorThrown: (state: boolean) => void;
|
||||
@@ -42,6 +40,7 @@ export interface TabsState {
|
||||
selectLeftTab: () => void;
|
||||
selectRightTab: () => void;
|
||||
closeActiveTab: () => void;
|
||||
closeAllTabs: () => void;
|
||||
persistTabsState: () => void;
|
||||
}
|
||||
|
||||
@@ -68,7 +67,6 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
openedReactTabs: !isPlatformFabric ? [ReactTabKind.Home] : [],
|
||||
activeTab: undefined,
|
||||
activeReactTab: !isPlatformFabric ? ReactTabKind.Home : undefined,
|
||||
networkSettingsWarning: "",
|
||||
queryCopilotTabInitialInput: "",
|
||||
isTabExecuting: false,
|
||||
isQueryErrorThrown: false,
|
||||
@@ -189,7 +187,6 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
|
||||
set({ openedReactTabs: updatedOpenedReactTabs });
|
||||
},
|
||||
setNetworkSettingsWarning: (warningMessage: string) => set({ networkSettingsWarning: warningMessage }),
|
||||
setQueryCopilotTabInitialInput: (input: string) => set({ queryCopilotTabInitialInput: input }),
|
||||
setIsTabExecuting: (state: boolean) => {
|
||||
set({ isTabExecuting: state });
|
||||
@@ -237,6 +234,9 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
state.closeTab(state.activeTab);
|
||||
}
|
||||
},
|
||||
closeAllTabs: () => {
|
||||
set({ openedTabs: [], openedReactTabs: [], activeTab: undefined, activeReactTab: undefined });
|
||||
},
|
||||
persistTabsState: () => {
|
||||
const state = get();
|
||||
const openTabsStates = state.openedTabs.map((tab) => tab.getPersistedState());
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { initializeIcons } from "@fluentui/react";
|
||||
// import { initializeIcons } from "@fluentui/react";
|
||||
import { configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
import "jest-canvas-mock";
|
||||
import enableHooks from "jest-react-hooks-shallow";
|
||||
import { TextDecoder, TextEncoder } from "util";
|
||||
configure({ adapter: new Adapter() });
|
||||
initializeIcons();
|
||||
// initializeIcons();
|
||||
|
||||
if (typeof window.URL.createObjectURL === "undefined") {
|
||||
Object.defineProperty(window.URL, "createObjectURL", { value: () => {} });
|
||||
|
||||
@@ -193,7 +193,12 @@ module.exports = function (_env = {}, argv = {}) {
|
||||
];
|
||||
|
||||
if (argv.analyze) {
|
||||
plugins.push(new BundleAnalyzerPlugin());
|
||||
plugins.push(
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: "bundle-report.html",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -256,6 +261,17 @@ module.exports = function (_env = {}, argv = {}) {
|
||||
},
|
||||
}),
|
||||
],
|
||||
splitChunks: {
|
||||
chunks: "all",
|
||||
cacheGroups: {
|
||||
fluentIcons: {
|
||||
test: /[\\/]node_modules[\\/]@fluentui[\\/](font-icons-mdl2|react-icons)/,
|
||||
name: "fluent-icons",
|
||||
chunks: "all",
|
||||
enforce: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: false,
|
||||
// Hack since it is hard to disable watch entirely with webpack dev server https://github.com/webpack/webpack-dev-server/issues/1251#issuecomment-654240734
|
||||
|
||||
Reference in New Issue
Block a user