diff --git a/package-lock.json b/package-lock.json index e18653826..bef67e871 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "dependencies": { "@azure/arm-cosmosdb": "9.1.0", - "@azure/cosmos": "4.2.0-beta.1", + "@azure/cosmos": "4.3.0", "@azure/cosmos-language-service": "0.0.5", "@azure/identity": "4.5.0", "@azure/msal-browser": "2.14.2", @@ -290,57 +290,69 @@ "version": "2.6.2", "license": "0BSD" }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz", - "integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==", + "node_modules/@azure/core-http-compat": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz", + "integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==", "dependencies": { "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.8.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.11.0", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.20.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@azure/core-rest-pipeline/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "node_modules/@azure/core-lro/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", "dependencies": { - "debug": "^4.3.4" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-rest-pipeline/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } + "node_modules/@azure/core-paging/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "node_modules/@azure/core-rest-pipeline": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.20.0.tgz", + "integrity": "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g==", "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@typespec/ts-http-runtime": "^0.2.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 14" + "node": ">=18.0.0" } }, "node_modules/@azure/core-rest-pipeline/node_modules/tslib": { @@ -379,15 +391,16 @@ "license": "0BSD" }, "node_modules/@azure/cosmos": { - "version": "4.2.0-beta.1", - "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz", - "integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz", + "integrity": "sha512-0Ls3l1uWBBSphx6YRhnM+w7rSvq8qVugBCdO6kSiNuRYXEf6+YWLjbzz4e7L2kkz/6ScFdZIOJYP+XtkiRYOhA==", "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.7.1", "@azure/core-rest-pipeline": "^1.15.1", "@azure/core-tracing": "^1.1.1", "@azure/core-util": "^1.8.1", + "@azure/keyvault-keys": "^4.8.0", "fast-json-stable-stringify": "^2.1.0", "jsbi": "^4.3.0", "priorityqueuejs": "^2.0.0", @@ -492,14 +505,66 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "node_modules/@azure/logger": { - "version": "1.0.4", - "license": "MIT", + "node_modules/@azure/keyvault-common": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-common/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@azure/keyvault-keys": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.9.0.tgz", + "integrity": "sha512-ZBP07+K4Pj3kS4TF4XdkqFcspWwBHry3vJSOFM5k5ZABvf7JfiMonvaFk2nBF6xjlEbMpz5PE1g45iTMme0raQ==", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^2.0.1", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/keyvault-common": "^2.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-keys/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@azure/logger": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.2.0.tgz", + "integrity": "sha512-0hKEzLhpw+ZTAfNJyRrn6s+V0nDWzXk9OjBr2TiGIu0OfMr5s2V4FpKLTAK3Ca5r5OKLbf4hkOGDPyiRjie/jA==", + "dependencies": { + "@typespec/ts-http-runtime": "^0.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@azure/logger/node_modules/tslib": { @@ -13074,6 +13139,56 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.2.2.tgz", + "integrity": "sha512-Gz/Sm64+Sq/vklJu1tt9t+4R2lvnud8NbTD/ZfpZtMiUX7YeVpCA8j6NSW8ptwcoLL+NmYANwqP8DV0q/bwl2w==", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@ungap/url-search-params": { "version": "0.2.2", "license": "ISC" diff --git a/package.json b/package.json index b5914047b..faf4cdac9 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "dependencies": { "@azure/arm-cosmosdb": "9.1.0", - "@azure/cosmos": "4.2.0-beta.1", + "@azure/cosmos": "4.3.0", "@azure/cosmos-language-service": "0.0.5", "@azure/identity": "4.5.0", "@azure/msal-browser": "2.14.2", diff --git a/preview/package-lock.json b/preview/package-lock.json index daf213a84..ab6c6ecbb 100644 --- a/preview/package-lock.json +++ b/preview/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "dependencies": { "@azure/arm-cosmosdb": "9.1.0", - "@azure/cosmos": "4.2.0-beta.1", + "@azure/cosmos": "4.3.0", "@azure/cosmos-language-service": "0.0.5", "@azure/identity": "4.5.0", "@azure/msal-browser": "2.14.2", @@ -377,8 +377,8 @@ "license": "0BSD" }, "node_modules/@azure/cosmos": { - "version": "4.2.0-beta.1", - "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz", "integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==", "dependencies": { "@azure/abort-controller": "^2.0.0", diff --git a/src/Common/IteratorUtilities.ts b/src/Common/IteratorUtilities.ts index 0cbe8ea63..827033fd7 100644 --- a/src/Common/IteratorUtilities.ts +++ b/src/Common/IteratorUtilities.ts @@ -1,4 +1,3 @@ -import { QueryOperationOptions } from "@azure/cosmos"; import { Action } from "Shared/Telemetry/TelemetryConstants"; import * as Constants from "../Common/Constants"; import { QueryResults } from "../Contracts/ViewModels"; @@ -14,18 +13,14 @@ interface QueryResponse { } export interface MinimalQueryIterator { - fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise; + fetchNext: () => Promise; } // Pick, "fetchNext">; -export function nextPage( - documentsIterator: MinimalQueryIterator, - firstItemIndex: number, - queryOperationOptions?: QueryOperationOptions, -): Promise { +export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise { TelemetryProcessor.traceStart(Action.ExecuteQuery); - return documentsIterator.fetchNext(queryOperationOptions).then((response) => { + return documentsIterator.fetchNext().then((response) => { TelemetryProcessor.traceSuccess(Action.ExecuteQuery, { dataExplorerArea: Constants.Areas.Tab }); const documents = response.resources; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/Common/QueryError.ts b/src/Common/QueryError.ts index c1bab1c02..de84deee7 100644 --- a/src/Common/QueryError.ts +++ b/src/Common/QueryError.ts @@ -1,5 +1,4 @@ import { monaco } from "Explorer/LazyMonaco"; -import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility"; export enum QueryErrorSeverity { Error = "Error", @@ -103,20 +102,9 @@ export interface ErrorEnrichment { learnMoreUrl?: string; } -const REPLACEMENT_MESSAGES: Record string> = { - OPERATION_RU_LIMIT_EXCEEDED: (original) => { - if (ruThresholdEnabled()) { - const threshold = getRUThreshold(); - return `Query exceeded the Request Unit (RU) limit of ${threshold} RUs. You can change this limit in Data Explorer settings.`; - } - return original; - }, -}; +const REPLACEMENT_MESSAGES: Record string> = {}; -const HELP_LINKS: Record = { - OPERATION_RU_LIMIT_EXCEEDED: - "https://learn.microsoft.com/en-us/azure/cosmos-db/data-explorer#configure-request-unit-threshold", -}; +const HELP_LINKS: Record = {}; export default class QueryError { message: string; diff --git a/src/Common/dataAccess/__snapshots__/queryDocuments.test.ts.snap b/src/Common/dataAccess/__snapshots__/queryDocuments.test.ts.snap index bcf0034a4..0c7f67c57 100644 --- a/src/Common/dataAccess/__snapshots__/queryDocuments.test.ts.snap +++ b/src/Common/dataAccess/__snapshots__/queryDocuments.test.ts.snap @@ -3,6 +3,7 @@ exports[`getCommonQueryOptions builds the correct default options objects 1`] = ` { "disableNonStreamingOrderByQuery": true, + "enableQueryControl": false, "enableScanInQuery": true, "forceQueryPlan": true, "maxDegreeOfParallelism": 0, @@ -14,6 +15,7 @@ exports[`getCommonQueryOptions builds the correct default options objects 1`] = exports[`getCommonQueryOptions reads from localStorage 1`] = ` { "disableNonStreamingOrderByQuery": true, + "enableQueryControl": false, "enableScanInQuery": true, "forceQueryPlan": true, "maxDegreeOfParallelism": 17, diff --git a/src/Common/dataAccess/queryDocuments.ts b/src/Common/dataAccess/queryDocuments.ts index 223fe987d..44cd6f4e9 100644 --- a/src/Common/dataAccess/queryDocuments.ts +++ b/src/Common/dataAccess/queryDocuments.ts @@ -26,6 +26,7 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => { options.maxItemCount || (storedItemPerPageSetting !== undefined && storedItemPerPageSetting) || Queries.itemsPerPage; + options.enableQueryControl = LocalStorageUtility.getEntryBoolean(StorageKey.QueryControlEnabled); options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism); options.disableNonStreamingOrderByQuery = !isVectorSearchEnabled(); return options; diff --git a/src/Common/dataAccess/queryDocumentsPage.ts b/src/Common/dataAccess/queryDocumentsPage.ts index 17e84ba28..556ed290c 100644 --- a/src/Common/dataAccess/queryDocumentsPage.ts +++ b/src/Common/dataAccess/queryDocumentsPage.ts @@ -1,4 +1,3 @@ -import { QueryOperationOptions } from "@azure/cosmos"; import { QueryResults } from "../../Contracts/ViewModels"; import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { getEntityName } from "../DocumentUtility"; @@ -9,13 +8,12 @@ export const queryDocumentsPage = async ( resourceName: string, documentsIterator: MinimalQueryIterator, firstItemIndex: number, - queryOperationOptions?: QueryOperationOptions, ): Promise => { 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; diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index 403eb9e79..a49ab779b 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -50,6 +50,7 @@ export interface QueryResults extends QueryResultsMetadata { roundTrips?: number; headers?: any; queryMetrics?: QueryMetrics; + ruThresholdExceeded?: boolean; } export interface Button { diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx index 5ca296b57..0924a06b3 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx @@ -16,7 +16,12 @@ import * as StorageUtility from "../../../Shared/StorageUtility"; import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; -import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils"; +import { + logConsoleError, + logConsoleInfo, + logConsoleProgress, + logConsoleWarning, +} from "../../../Utils/NotificationConsoleUtils"; import { EditorReact } from "../../Controls/Editor/EditorReact"; import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent"; import * as TabComponent from "../../Controls/Tabs/TabComponent"; @@ -1083,6 +1088,7 @@ export class GraphExplorer extends React.Component void; public static reportToConsole(type: ConsoleDataType.Info, msg: string, ...errorData: any[]): void; public static reportToConsole(type: ConsoleDataType.Error, msg: string, ...errorData: any[]): void; + public static reportToConsole(type: ConsoleDataType.Warning, msg: string, ...errorData: any[]): void; public static reportToConsole(type: ConsoleDataType, msg: string, ...errorData: any[]): void | (() => void) { let errorDataStr = ""; if (errorData && errorData.length > 0) { @@ -1099,6 +1105,8 @@ export class GraphExplorer extends React.Component data.type === ConsoleDataType.Info, ).length; + const numWarningItems = this.state.allConsoleData.filter( + (data: ConsoleData) => data.type === ConsoleDataType.Warning, + ).length; return (
@@ -118,6 +122,10 @@ export class NotificationConsoleComponent extends React.Component< Info items {numInfoItems} + + Warning items + {numWarningItems} + {userContext.features.pr && } @@ -198,6 +206,7 @@ export class NotificationConsoleComponent extends React.Component< {item.type === ConsoleDataType.Info && info} {item.type === ConsoleDataType.Error && error} {item.type === ConsoleDataType.InProgress && in progress} + {item.type === ConsoleDataType.Warning && warning} {item.date} {item.message} diff --git a/src/Explorer/Menus/NotificationConsole/__snapshots__/NotificationConsoleComponent.test.tsx.snap b/src/Explorer/Menus/NotificationConsole/__snapshots__/NotificationConsoleComponent.test.tsx.snap index b2672aeec..5d471f177 100644 --- a/src/Explorer/Menus/NotificationConsole/__snapshots__/NotificationConsoleComponent.test.tsx.snap +++ b/src/Explorer/Menus/NotificationConsole/__snapshots__/NotificationConsoleComponent.test.tsx.snap @@ -59,6 +59,19 @@ exports[`NotificationConsoleComponent renders the console 1`] = ` 0 + + Warning items + + 0 + + + + Warning items + + 0 + + = ({ ? LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds) : Constants.Queries.DefaultMaxWaitTimeInSeconds, ); + const [queryControlEnabled, setQueryControlEnabled] = useState( + LocalStorageUtility.hasItem(StorageKey.QueryControlEnabled) + ? LocalStorageUtility.getEntryString(StorageKey.QueryControlEnabled) === "true" + : false, + ); const [maxDegreeOfParallelism, setMaxDegreeOfParallelism] = useState( LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism) ? LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism) @@ -204,6 +209,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ !isEmulator; const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin" && !isEmulator; const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin" && !isEmulator; + const shouldShowEnhancedQueryControl = userContext.apiType === "SQL"; const shouldShowParallelismOption = userContext.apiType !== "Gremlin" && !isEmulator; const showEnableEntraIdRbac = isDataplaneRbacSupported(userContext.apiType) && @@ -381,6 +387,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ LocalStorageUtility.setEntryNumber(StorageKey.MaxWaitTimeInSeconds, MaxWaitTimeInSeconds); LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString()); LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString()); + LocalStorageUtility.setEntryString(StorageKey.QueryControlEnabled, queryControlEnabled.toString()); LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism); LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString()); LocalStorageUtility.setEntryString(StorageKey.CopilotSampleDBEnabled, copilotSampleDBEnabled.toString()); @@ -410,6 +417,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ `Updated items per page setting to ${LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage)}`, ); logConsoleInfo(`${crossPartitionQueryEnabled ? "Enabled" : "Disabled"} cross-partition query feed option`); + logConsoleInfo(`${queryControlEnabled ? "Enabled" : "Disabled"} query control option`); logConsoleInfo( `Updated the max degree of parallelism query feed option to ${LocalStorageUtility.getEntryNumber( StorageKey.MaxDegreeOfParellism, @@ -760,7 +768,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ )} -
RU Limit
@@ -943,6 +950,38 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
)} + {shouldShowEnhancedQueryControl && ( + + +
Enhanced query control
+
+ +
+
+ Query up to the max degree of parallelism. + + {" "} + Learn more{" "} + +
+ setQueryControlEnabled(!queryControlEnabled)} + label="Enable query control" + /> +
+
+
+ )} {shouldShowParallelismOption && ( diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap index 41a75a38f..41759f1c7 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap @@ -495,6 +495,51 @@ exports[`Settings Pane should render Default properly 1`] = `
+ + +
+ Enhanced query control +
+
+ +
+
+ Query up to the max degree of parallelism. + + + Learn more + + +
+ +
+
+
diff --git a/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx b/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx index 747ff3349..c55923486 100644 --- a/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx +++ b/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx @@ -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 { AuthType } from "AuthType"; import QueryError, { createMonacoErrorLocationResolver, createMonacoMarkersForQueryErrors } from "Common/QueryError"; import { SplitterDirection } from "Common/Splitter"; @@ -19,7 +19,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 { useClientWriteEnabled } from "hooks/useClientWriteEnabled"; @@ -369,22 +369,9 @@ class QueryTabComponentImpl extends React.Component - await queryDocumentsPage( - this.props.collection && this.props.collection.id(), - this._iterator, - firstItemIndex, - queryOperationOptions, - ); + await queryDocumentsPage(this.props.collection && this.props.collection.id(), this._iterator, firstItemIndex); this.props.tabsBaseInstance.isExecuting(true); this.setState({ isExecuting: true, @@ -424,6 +411,9 @@ class QueryTabComponentImpl extends React.Component {useObservable(tab?.isExecutionError || ko.observable(false)) && } + {useObservable(tab?.isExecutionWarning || ko.observable(false)) && ( + + )} {isTabExecuting(tab, tabKind) && ( Loading )} @@ -194,6 +198,20 @@ const ErrorIcon = ({ tab, active }: { tab: Tab; active: boolean }) => ( ); +const WarningIcon = ({ tab, active }: { tab: Tab; active: boolean }) => ( +
tab.onErrorDetailsClick(undefined, e)} + onKeyPress={({ nativeEvent: e }) => tab.onErrorDetailsKeyPress(undefined, e)} + > + Warning Icon +
+); + function TabPane({ tab, active }: { tab: Tab; active: boolean }) { const ref = useRef(); const attrs = { diff --git a/src/Explorer/Tabs/TabsBase.ts b/src/Explorer/Tabs/TabsBase.ts index 4d82d66a4..2b97fed3e 100644 --- a/src/Explorer/Tabs/TabsBase.ts +++ b/src/Explorer/Tabs/TabsBase.ts @@ -27,6 +27,7 @@ export default class TabsBase extends WaitsForTemplateViewModel { public tabTitle: ko.Observable; public tabPath: ko.Observable; public isExecutionError = ko.observable(false); + public isExecutionWarning = ko.observable(false); public isExecuting = ko.observable(false); protected _theme: string; public onLoadStartKey: number; diff --git a/src/Shared/StorageUtility.ts b/src/Shared/StorageUtility.ts index f2baa85da..c5b2f18dd 100644 --- a/src/Shared/StorageUtility.ts +++ b/src/Shared/StorageUtility.ts @@ -21,6 +21,7 @@ export enum StorageKey { DatabaseAccountId, EncryptedKeyToken, IsCrossPartitionQueryEnabled, + QueryControlEnabled, MaxDegreeOfParellism, IsGraphAutoVizDisabled, TenantId, diff --git a/src/Utils/NotificationConsoleUtils.ts b/src/Utils/NotificationConsoleUtils.ts index b3c63e5a7..927c3c490 100644 --- a/src/Utils/NotificationConsoleUtils.ts +++ b/src/Utils/NotificationConsoleUtils.ts @@ -23,3 +23,7 @@ export const logConsoleError = (msg: string): void => { export const logConsoleInfo = (msg: string): void => { log(ConsoleDataType.Info, msg); }; + +export const logConsoleWarning = (msg: string): void => { + log(ConsoleDataType.Warning, msg); +}; diff --git a/src/Utils/QueryUtils.ts b/src/Utils/QueryUtils.ts index 643dd526b..08c7a73d0 100644 --- a/src/Utils/QueryUtils.ts +++ b/src/Utils/QueryUtils.ts @@ -1,4 +1,7 @@ import { PartitionKey, PartitionKeyDefinition } from "@azure/cosmos"; +import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility"; +import { userContext } from "UserContext"; +import { logConsoleWarning } from "Utils/NotificationConsoleUtils"; import * as DataModels from "../Contracts/DataModels"; import * as ViewModels from "../Contracts/ViewModels"; @@ -86,6 +89,18 @@ export const queryPagesUntilContentPresent = async ( results.roundTrips = roundTrips; results.requestCharge = Number(results.requestCharge) + netRequestCharge; netRequestCharge = Number(results.requestCharge); + + if (results.hasMoreResults && userContext.apiType === "SQL" && ruThresholdEnabled()) { + const ruThreshold: number = getRUThreshold(); + if (netRequestCharge > ruThreshold) { + logConsoleWarning( + `Warning: Query has exceeded the Request Unit threshold of ${ruThreshold} RUs. Query results show only those documents returned before the threshold was exceeded`, + ); + results.ruThresholdExceeded = true; + return results; + } + } + const resultsMetadata = { hasMoreResults: results.hasMoreResults, itemCount: results.itemCount, diff --git a/webpack.config.js b/webpack.config.js index 21858c2b8..917d4ca87 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -78,11 +78,18 @@ const typescriptRule = { exclude: /node_modules/, }; +const javascriptRule = { + test: /\.m?js$/, + resolve: { + fullySpecified: false, + }, +}; + // eslint-disable-next-line @typescript-eslint/no-unused-vars /** @type {(_env: Record, argv: Record) => import("webpack").Configuration} */ module.exports = function (_env = {}, argv = {}) { const mode = argv.mode || "development"; - const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule]; + const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule, javascriptRule]; const envVars = { GIT_SHA: gitSha, PORT: process.env.PORT || "1234",