Upgrade Cosmos SDK to v4.3 (#2137)

* Upgrade Cosmos SDK to v4.3

* push pkg lock

* fix tests

* fix package-lock.json

* fix package-lock

* fix package-lock.json

* fix package-lock.json

* log console warning when RU limit is reached

* fix tests

* fix format

* fix tests

* added description to RU limit message

* show warning icon on tab header

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
This commit is contained in:
asier-isayas 2025-05-29 11:35:06 -04:00 committed by GitHub
parent c5ed537109
commit 0fc6647627
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 354 additions and 90 deletions

197
package-lock.json generated
View File

@ -10,7 +10,7 @@
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@azure/arm-cosmosdb": "9.1.0", "@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/cosmos-language-service": "0.0.5",
"@azure/identity": "4.5.0", "@azure/identity": "4.5.0",
"@azure/msal-browser": "2.14.2", "@azure/msal-browser": "2.14.2",
@ -290,57 +290,69 @@
"version": "2.6.2", "version": "2.6.2",
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/@azure/core-rest-pipeline": { "node_modules/@azure/core-http-compat": {
"version": "1.18.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz", "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz",
"integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==", "integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==",
"dependencies": { "dependencies": {
"@azure/abort-controller": "^2.0.0", "@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.8.0", "@azure/core-client": "^1.3.0",
"@azure/core-tracing": "^1.0.1", "@azure/core-rest-pipeline": "^1.20.0"
"@azure/core-util": "^1.11.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", "@azure/logger": "^1.0.0",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.0",
"tslib": "^2.6.2" "tslib": "^2.6.2"
}, },
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
} }
}, },
"node_modules/@azure/core-rest-pipeline/node_modules/agent-base": { "node_modules/@azure/core-lro/node_modules/tslib": {
"version": "7.1.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "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": { "dependencies": {
"debug": "^4.3.4" "tslib": "^2.6.2"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">=18.0.0"
} }
}, },
"node_modules/@azure/core-rest-pipeline/node_modules/http-proxy-agent": { "node_modules/@azure/core-paging/node_modules/tslib": {
"version": "7.0.2", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
}, },
"node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent": { "node_modules/@azure/core-rest-pipeline": {
"version": "7.0.5", "version": "1.20.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.20.0.tgz",
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "integrity": "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g==",
"dependencies": { "dependencies": {
"agent-base": "^7.0.2", "@azure/abort-controller": "^2.0.0",
"debug": "4" "@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": { "engines": {
"node": ">= 14" "node": ">=18.0.0"
} }
}, },
"node_modules/@azure/core-rest-pipeline/node_modules/tslib": { "node_modules/@azure/core-rest-pipeline/node_modules/tslib": {
@ -379,15 +391,16 @@
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/@azure/cosmos": { "node_modules/@azure/cosmos": {
"version": "4.2.0-beta.1", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz", "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz",
"integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==", "integrity": "sha512-0Ls3l1uWBBSphx6YRhnM+w7rSvq8qVugBCdO6kSiNuRYXEf6+YWLjbzz4e7L2kkz/6ScFdZIOJYP+XtkiRYOhA==",
"dependencies": { "dependencies": {
"@azure/abort-controller": "^2.0.0", "@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.7.1", "@azure/core-auth": "^1.7.1",
"@azure/core-rest-pipeline": "^1.15.1", "@azure/core-rest-pipeline": "^1.15.1",
"@azure/core-tracing": "^1.1.1", "@azure/core-tracing": "^1.1.1",
"@azure/core-util": "^1.8.1", "@azure/core-util": "^1.8.1",
"@azure/keyvault-keys": "^4.8.0",
"fast-json-stable-stringify": "^2.1.0", "fast-json-stable-stringify": "^2.1.0",
"jsbi": "^4.3.0", "jsbi": "^4.3.0",
"priorityqueuejs": "^2.0.0", "priorityqueuejs": "^2.0.0",
@ -492,14 +505,66 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
}, },
"node_modules/@azure/logger": { "node_modules/@azure/keyvault-common": {
"version": "1.0.4", "version": "2.0.0",
"license": "MIT", "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz",
"integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==",
"dependencies": { "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" "tslib": "^2.2.0"
}, },
"engines": { "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": { "node_modules/@azure/logger/node_modules/tslib": {
@ -13074,6 +13139,56 @@
"url": "https://opencollective.com/typescript-eslint" "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": { "node_modules/@ungap/url-search-params": {
"version": "0.2.2", "version": "0.2.2",
"license": "ISC" "license": "ISC"

View File

@ -5,7 +5,7 @@
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"@azure/arm-cosmosdb": "9.1.0", "@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/cosmos-language-service": "0.0.5",
"@azure/identity": "4.5.0", "@azure/identity": "4.5.0",
"@azure/msal-browser": "2.14.2", "@azure/msal-browser": "2.14.2",

View File

@ -10,7 +10,7 @@
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@azure/arm-cosmosdb": "9.1.0", "@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/cosmos-language-service": "0.0.5",
"@azure/identity": "4.5.0", "@azure/identity": "4.5.0",
"@azure/msal-browser": "2.14.2", "@azure/msal-browser": "2.14.2",
@ -377,8 +377,8 @@
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/@azure/cosmos": { "node_modules/@azure/cosmos": {
"version": "4.2.0-beta.1", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz", "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz",
"integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==", "integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==",
"dependencies": { "dependencies": {
"@azure/abort-controller": "^2.0.0", "@azure/abort-controller": "^2.0.0",

View File

@ -1,4 +1,3 @@
import { QueryOperationOptions } from "@azure/cosmos";
import { Action } from "Shared/Telemetry/TelemetryConstants"; import { Action } from "Shared/Telemetry/TelemetryConstants";
import * as Constants from "../Common/Constants"; import * as Constants from "../Common/Constants";
import { QueryResults } from "../Contracts/ViewModels"; import { QueryResults } from "../Contracts/ViewModels";
@ -14,18 +13,14 @@ interface QueryResponse {
} }
export interface MinimalQueryIterator { export interface MinimalQueryIterator {
fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>; fetchNext: () => Promise<QueryResponse>;
} }
// Pick<QueryIterator<any>, "fetchNext">; // Pick<QueryIterator<any>, "fetchNext">;
export function nextPage( export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
queryOperationOptions?: QueryOperationOptions,
): Promise<QueryResults> {
TelemetryProcessor.traceStart(Action.ExecuteQuery); TelemetryProcessor.traceStart(Action.ExecuteQuery);
return documentsIterator.fetchNext(queryOperationOptions).then((response) => { return documentsIterator.fetchNext().then((response) => {
TelemetryProcessor.traceSuccess(Action.ExecuteQuery, { dataExplorerArea: Constants.Areas.Tab }); TelemetryProcessor.traceSuccess(Action.ExecuteQuery, { dataExplorerArea: Constants.Areas.Tab });
const documents = response.resources; const documents = response.resources;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -1,5 +1,4 @@
import { monaco } from "Explorer/LazyMonaco"; import { monaco } from "Explorer/LazyMonaco";
import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
export enum QueryErrorSeverity { export enum QueryErrorSeverity {
Error = "Error", Error = "Error",
@ -103,20 +102,9 @@ export interface ErrorEnrichment {
learnMoreUrl?: string; learnMoreUrl?: string;
} }
const REPLACEMENT_MESSAGES: Record<string, (original: string) => string> = { const REPLACEMENT_MESSAGES: Record<string, (original: string) => 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 HELP_LINKS: Record<string, string> = { const HELP_LINKS: Record<string, string> = {};
OPERATION_RU_LIMIT_EXCEEDED:
"https://learn.microsoft.com/en-us/azure/cosmos-db/data-explorer#configure-request-unit-threshold",
};
export default class QueryError { export default class QueryError {
message: string; message: string;

View File

@ -3,6 +3,7 @@
exports[`getCommonQueryOptions builds the correct default options objects 1`] = ` exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
{ {
"disableNonStreamingOrderByQuery": true, "disableNonStreamingOrderByQuery": true,
"enableQueryControl": false,
"enableScanInQuery": true, "enableScanInQuery": true,
"forceQueryPlan": true, "forceQueryPlan": true,
"maxDegreeOfParallelism": 0, "maxDegreeOfParallelism": 0,
@ -14,6 +15,7 @@ exports[`getCommonQueryOptions builds the correct default options objects 1`] =
exports[`getCommonQueryOptions reads from localStorage 1`] = ` exports[`getCommonQueryOptions reads from localStorage 1`] = `
{ {
"disableNonStreamingOrderByQuery": true, "disableNonStreamingOrderByQuery": true,
"enableQueryControl": false,
"enableScanInQuery": true, "enableScanInQuery": true,
"forceQueryPlan": true, "forceQueryPlan": true,
"maxDegreeOfParallelism": 17, "maxDegreeOfParallelism": 17,

View File

@ -26,6 +26,7 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
options.maxItemCount || options.maxItemCount ||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) || (storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Queries.itemsPerPage; Queries.itemsPerPage;
options.enableQueryControl = LocalStorageUtility.getEntryBoolean(StorageKey.QueryControlEnabled);
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism); options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
options.disableNonStreamingOrderByQuery = !isVectorSearchEnabled(); options.disableNonStreamingOrderByQuery = !isVectorSearchEnabled();
return options; return options;

View File

@ -1,4 +1,3 @@
import { QueryOperationOptions } from "@azure/cosmos";
import { QueryResults } from "../../Contracts/ViewModels"; import { QueryResults } from "../../Contracts/ViewModels";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { getEntityName } from "../DocumentUtility"; import { getEntityName } from "../DocumentUtility";
@ -9,13 +8,12 @@ export const queryDocumentsPage = async (
resourceName: string, resourceName: string,
documentsIterator: MinimalQueryIterator, documentsIterator: MinimalQueryIterator,
firstItemIndex: number, firstItemIndex: number,
queryOperationOptions?: QueryOperationOptions,
): Promise<QueryResults> => { ): Promise<QueryResults> => {
const entityName = getEntityName(); const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`); const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
try { try {
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex, queryOperationOptions); const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
const itemCount = (result.documents && result.documents.length) || 0; const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`); logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
return result; return result;

View File

@ -50,6 +50,7 @@ export interface QueryResults extends QueryResultsMetadata {
roundTrips?: number; roundTrips?: number;
headers?: any; headers?: any;
queryMetrics?: QueryMetrics; queryMetrics?: QueryMetrics;
ruThresholdExceeded?: boolean;
} }
export interface Button { export interface Button {

View File

@ -16,7 +16,12 @@ import * as StorageUtility from "../../../Shared/StorageUtility";
import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility"; import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; 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 { EditorReact } from "../../Controls/Editor/EditorReact";
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent"; import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
import * as TabComponent from "../../Controls/Tabs/TabComponent"; import * as TabComponent from "../../Controls/Tabs/TabComponent";
@ -1083,6 +1088,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
public static reportToConsole(type: ConsoleDataType.InProgress, msg: string, ...errorData: any[]): () => void; public static reportToConsole(type: ConsoleDataType.InProgress, msg: string, ...errorData: any[]): () => void;
public static reportToConsole(type: ConsoleDataType.Info, msg: string, ...errorData: any[]): 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.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) { public static reportToConsole(type: ConsoleDataType, msg: string, ...errorData: any[]): void | (() => void) {
let errorDataStr = ""; let errorDataStr = "";
if (errorData && errorData.length > 0) { if (errorData && errorData.length > 0) {
@ -1099,6 +1105,8 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
return logConsoleInfo(consoleMessage); return logConsoleInfo(consoleMessage);
case ConsoleDataType.InProgress: case ConsoleDataType.InProgress:
return logConsoleProgress(consoleMessage); return logConsoleProgress(consoleMessage);
case ConsoleDataType.Warning:
return logConsoleWarning(consoleMessage);
} }
} }

View File

@ -13,4 +13,5 @@ export enum ConsoleDataType {
Info = 0, Info = 0,
Error = 1, Error = 1,
InProgress = 2, InProgress = 2,
Warning = 3,
} }

View File

@ -14,6 +14,7 @@ import ErrorRedIcon from "../../../../images/error_red.svg";
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg"; import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
import InfoIcon from "../../../../images/info_color.svg"; import InfoIcon from "../../../../images/info_color.svg";
import LoadingIcon from "../../../../images/loading.svg"; import LoadingIcon from "../../../../images/loading.svg";
import WarningIcon from "../../../../images/warning.svg";
import { ClientDefaults, KeyCodes } from "../../../Common/Constants"; import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";
import { useNotificationConsole } from "../../../hooks/useNotificationConsole"; import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
@ -91,6 +92,9 @@ export class NotificationConsoleComponent extends React.Component<
const numInfoItems = this.state.allConsoleData.filter( const numInfoItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Info, (data: ConsoleData) => data.type === ConsoleDataType.Info,
).length; ).length;
const numWarningItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Warning,
).length;
return ( return (
<div className="notificationConsoleContainer"> <div className="notificationConsoleContainer">
@ -118,6 +122,10 @@ export class NotificationConsoleComponent extends React.Component<
<img src={infoBubbleIcon} alt="Info items" /> <img src={infoBubbleIcon} alt="Info items" />
<span className="numInfoItems">{numInfoItems}</span> <span className="numInfoItems">{numInfoItems}</span>
</span> </span>
<span className="notificationConsoleHeaderIconWithData">
<img src={WarningIcon} alt="Warning items" />
<span className="numWarningItems">{numWarningItems}</span>
</span>
</span> </span>
{userContext.features.pr && <PrPreview pr={userContext.features.pr} />} {userContext.features.pr && <PrPreview pr={userContext.features.pr} />}
<span className="consoleSplitter" /> <span className="consoleSplitter" />
@ -198,6 +206,7 @@ export class NotificationConsoleComponent extends React.Component<
{item.type === ConsoleDataType.Info && <img className="infoIcon" src={InfoIcon} alt="info" />} {item.type === ConsoleDataType.Info && <img className="infoIcon" src={InfoIcon} alt="info" />}
{item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />} {item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
{item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />} {item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
{item.type === ConsoleDataType.Warning && <img className="warningIcon" src={WarningIcon} alt="warning" />}
<span className="date">{item.date}</span> <span className="date">{item.date}</span>
<span className="message" role="alert" aria-live="assertive"> <span className="message" role="alert" aria-live="assertive">
{item.message} {item.message}

View File

@ -59,6 +59,19 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
0 0
</span> </span>
</span> </span>
<span
className="notificationConsoleHeaderIconWithData"
>
<img
alt="Warning items"
src={{}}
/>
<span
className="numWarningItems"
>
0
</span>
</span>
</span> </span>
<span <span
className="consoleSplitter" className="consoleSplitter"
@ -229,6 +242,19 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
1 1
</span> </span>
</span> </span>
<span
className="notificationConsoleHeaderIconWithData"
>
<img
alt="Warning items"
src={{}}
/>
<span
className="numWarningItems"
>
0
</span>
</span>
</span> </span>
<span <span
className="consoleSplitter" className="consoleSplitter"

View File

@ -180,6 +180,11 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
? LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds) ? LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds)
: Constants.Queries.DefaultMaxWaitTimeInSeconds, : Constants.Queries.DefaultMaxWaitTimeInSeconds,
); );
const [queryControlEnabled, setQueryControlEnabled] = useState<boolean>(
LocalStorageUtility.hasItem(StorageKey.QueryControlEnabled)
? LocalStorageUtility.getEntryString(StorageKey.QueryControlEnabled) === "true"
: false,
);
const [maxDegreeOfParallelism, setMaxDegreeOfParallelism] = useState<number>( const [maxDegreeOfParallelism, setMaxDegreeOfParallelism] = useState<number>(
LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism) LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism)
? LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism) ? LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism)
@ -204,6 +209,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
!isEmulator; !isEmulator;
const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin" && !isEmulator; const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin" && !isEmulator;
const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin" && !isEmulator; const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin" && !isEmulator;
const shouldShowEnhancedQueryControl = userContext.apiType === "SQL";
const shouldShowParallelismOption = userContext.apiType !== "Gremlin" && !isEmulator; const shouldShowParallelismOption = userContext.apiType !== "Gremlin" && !isEmulator;
const showEnableEntraIdRbac = const showEnableEntraIdRbac =
isDataplaneRbacSupported(userContext.apiType) && isDataplaneRbacSupported(userContext.apiType) &&
@ -381,6 +387,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
LocalStorageUtility.setEntryNumber(StorageKey.MaxWaitTimeInSeconds, MaxWaitTimeInSeconds); LocalStorageUtility.setEntryNumber(StorageKey.MaxWaitTimeInSeconds, MaxWaitTimeInSeconds);
LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString()); LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString());
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString()); LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString());
LocalStorageUtility.setEntryString(StorageKey.QueryControlEnabled, queryControlEnabled.toString());
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism); LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism);
LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString()); LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString());
LocalStorageUtility.setEntryString(StorageKey.CopilotSampleDBEnabled, copilotSampleDBEnabled.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)}`, `Updated items per page setting to ${LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage)}`,
); );
logConsoleInfo(`${crossPartitionQueryEnabled ? "Enabled" : "Disabled"} cross-partition query feed option`); logConsoleInfo(`${crossPartitionQueryEnabled ? "Enabled" : "Disabled"} cross-partition query feed option`);
logConsoleInfo(`${queryControlEnabled ? "Enabled" : "Disabled"} query control option`);
logConsoleInfo( logConsoleInfo(
`Updated the max degree of parallelism query feed option to ${LocalStorageUtility.getEntryNumber( `Updated the max degree of parallelism query feed option to ${LocalStorageUtility.getEntryNumber(
StorageKey.MaxDegreeOfParellism, StorageKey.MaxDegreeOfParellism,
@ -760,7 +768,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
)} )}
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>
<AccordionItem value="5"> <AccordionItem value="5">
<AccordionHeader> <AccordionHeader>
<div className={styles.header}>RU Limit</div> <div className={styles.header}>RU Limit</div>
@ -943,6 +950,38 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>
)} )}
{shouldShowEnhancedQueryControl && (
<AccordionItem value="10">
<AccordionHeader>
<div className={styles.header}>Enhanced query control</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
Query up to the max degree of parallelism.
<a
href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/performance-tips-query-sdk?tabs=v3&pivots=programming-language-nodejs#enhanced-query-control"
target="_blank"
rel="noopener noreferrer"
>
{" "}
Learn more{" "}
</a>
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="EnableQueryControl"
checked={queryControlEnabled}
onChange={() => setQueryControlEnabled(!queryControlEnabled)}
label="Enable query control"
/>
</div>
</AccordionPanel>
</AccordionItem>
)}
{shouldShowParallelismOption && ( {shouldShowParallelismOption && (
<AccordionItem value="10"> <AccordionItem value="10">
<AccordionHeader> <AccordionHeader>

View File

@ -495,6 +495,51 @@ exports[`Settings Pane should render Default properly 1`] = `
</div> </div>
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>
<AccordionItem
value="10"
>
<AccordionHeader>
<div
className="___15c001r_0000000 fq02s40"
>
Enhanced query control
</div>
</AccordionHeader>
<AccordionPanel>
<div
className="___1dfa554_0000000 fo7qwa0"
>
<div
className="___10gar1i_0000000 f1fow5ox f1ugzwwg"
>
Query up to the max degree of parallelism.
<a
href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/performance-tips-query-sdk?tabs=v3&pivots=programming-language-nodejs#enhanced-query-control"
rel="noopener noreferrer"
target="_blank"
>
Learn more
</a>
</div>
<StyledCheckboxBase
ariaLabel="EnableQueryControl"
checked={false}
className="padding"
label="Enable query control"
onChange={[Function]}
styles={
{
"label": {
"padding": 0,
},
}
}
/>
</div>
</AccordionPanel>
</AccordionItem>
<AccordionItem <AccordionItem
value="10" value="10"
> >

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-console */ /* eslint-disable no-console */
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos"; import { FeedOptions } from "@azure/cosmos";
import { AuthType } from "AuthType"; import { AuthType } from "AuthType";
import QueryError, { createMonacoErrorLocationResolver, createMonacoMarkersForQueryErrors } from "Common/QueryError"; import QueryError, { createMonacoErrorLocationResolver, createMonacoMarkersForQueryErrors } from "Common/QueryError";
import { SplitterDirection } from "Common/Splitter"; import { SplitterDirection } from "Common/Splitter";
@ -19,7 +19,7 @@ import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { useSelectedNode } from "Explorer/useSelectedNode"; import { useSelectedNode } from "Explorer/useSelectedNode";
import { KeyboardAction } from "KeyboardShortcuts"; import { KeyboardAction } from "KeyboardShortcuts";
import { QueryConstants } from "Shared/Constants"; 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 { Action } from "Shared/Telemetry/TelemetryConstants";
import { Allotment } from "allotment"; import { Allotment } from "allotment";
import { useClientWriteEnabled } from "hooks/useClientWriteEnabled"; import { useClientWriteEnabled } from "hooks/useClientWriteEnabled";
@ -369,22 +369,9 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
this.setState({ this.setState({
isExecutionError: false, isExecutionError: false,
}); });
this.props.tabsBaseInstance.isExecutionWarning(false);
let queryOperationOptions: QueryOperationOptions;
if (userContext.apiType === "SQL" && ruThresholdEnabled()) {
const ruThreshold: number = getRUThreshold();
queryOperationOptions = {
ruCapPerOperation: ruThreshold,
} as QueryOperationOptions;
}
const queryDocuments = async (firstItemIndex: number) => const queryDocuments = async (firstItemIndex: number) =>
await queryDocumentsPage( await queryDocumentsPage(this.props.collection && this.props.collection.id(), this._iterator, firstItemIndex);
this.props.collection && this.props.collection.id(),
this._iterator,
firstItemIndex,
queryOperationOptions,
);
this.props.tabsBaseInstance.isExecuting(true); this.props.tabsBaseInstance.isExecuting(true);
this.setState({ this.setState({
isExecuting: true, isExecuting: true,
@ -424,6 +411,9 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
firstItemIndex, firstItemIndex,
queryDocuments, queryDocuments,
); );
if (queryResults.ruThresholdExceeded) {
this.props.tabsBaseInstance.isExecutionWarning(true);
}
this.setState({ queryResults, errors: [] }); this.setState({ queryResults, errors: [] });
} catch (error) { } catch (error) {
this.props.tabsBaseInstance.isExecutionError(true); this.props.tabsBaseInstance.isExecutionError(true);

View File

@ -18,6 +18,7 @@ import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import loadingIcon from "../../../images/circular_loader_black_16x16.gif"; import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
import errorIcon from "../../../images/close-black.svg"; import errorIcon from "../../../images/close-black.svg";
import errorQuery from "../../../images/error_no_outline.svg"; import errorQuery from "../../../images/error_no_outline.svg";
import warningIconSvg from "../../../images/warning.svg";
import { useObservable } from "../../hooks/useObservable"; import { useObservable } from "../../hooks/useObservable";
import { ReactTabKind, useTabs } from "../../hooks/useTabs"; import { ReactTabKind, useTabs } from "../../hooks/useTabs";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
@ -117,6 +118,9 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
> >
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}> <span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />} {useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
{useObservable(tab?.isExecutionWarning || ko.observable(false)) && (
<WarningIcon tab={tab} active={active} />
)}
{isTabExecuting(tab, tabKind) && ( {isTabExecuting(tab, tabKind) && (
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" /> <img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
)} )}
@ -194,6 +198,20 @@ const ErrorIcon = ({ tab, active }: { tab: Tab; active: boolean }) => (
</div> </div>
); );
const WarningIcon = ({ tab, active }: { tab: Tab; active: boolean }) => (
<div
id="warningStatusIcon"
role="button"
title="Click to view more details"
tabIndex={active ? 0 : undefined}
className={active ? "actionsEnabled warningIconContainer" : "warningIconContainer"}
onClick={({ nativeEvent: e }) => tab.onErrorDetailsClick(undefined, e)}
onKeyPress={({ nativeEvent: e }) => tab.onErrorDetailsKeyPress(undefined, e)}
>
<img src={warningIconSvg} alt="Warning Icon" style={{ height: 15, marginBottom: 5 }} />
</div>
);
function TabPane({ tab, active }: { tab: Tab; active: boolean }) { function TabPane({ tab, active }: { tab: Tab; active: boolean }) {
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>();
const attrs = { const attrs = {

View File

@ -27,6 +27,7 @@ export default class TabsBase extends WaitsForTemplateViewModel {
public tabTitle: ko.Observable<string>; public tabTitle: ko.Observable<string>;
public tabPath: ko.Observable<string>; public tabPath: ko.Observable<string>;
public isExecutionError = ko.observable(false); public isExecutionError = ko.observable(false);
public isExecutionWarning = ko.observable(false);
public isExecuting = ko.observable(false); public isExecuting = ko.observable(false);
protected _theme: string; protected _theme: string;
public onLoadStartKey: number; public onLoadStartKey: number;

View File

@ -21,6 +21,7 @@ export enum StorageKey {
DatabaseAccountId, DatabaseAccountId,
EncryptedKeyToken, EncryptedKeyToken,
IsCrossPartitionQueryEnabled, IsCrossPartitionQueryEnabled,
QueryControlEnabled,
MaxDegreeOfParellism, MaxDegreeOfParellism,
IsGraphAutoVizDisabled, IsGraphAutoVizDisabled,
TenantId, TenantId,

View File

@ -23,3 +23,7 @@ export const logConsoleError = (msg: string): void => {
export const logConsoleInfo = (msg: string): void => { export const logConsoleInfo = (msg: string): void => {
log(ConsoleDataType.Info, msg); log(ConsoleDataType.Info, msg);
}; };
export const logConsoleWarning = (msg: string): void => {
log(ConsoleDataType.Warning, msg);
};

View File

@ -1,4 +1,7 @@
import { PartitionKey, PartitionKeyDefinition } from "@azure/cosmos"; 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 DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
@ -86,6 +89,18 @@ export const queryPagesUntilContentPresent = async (
results.roundTrips = roundTrips; results.roundTrips = roundTrips;
results.requestCharge = Number(results.requestCharge) + netRequestCharge; results.requestCharge = Number(results.requestCharge) + netRequestCharge;
netRequestCharge = Number(results.requestCharge); 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 = { const resultsMetadata = {
hasMoreResults: results.hasMoreResults, hasMoreResults: results.hasMoreResults,
itemCount: results.itemCount, itemCount: results.itemCount,

View File

@ -78,11 +78,18 @@ const typescriptRule = {
exclude: /node_modules/, exclude: /node_modules/,
}; };
const javascriptRule = {
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
/** @type {(_env: Record<string, string>, argv: Record<string, unknown>) => import("webpack").Configuration} */ /** @type {(_env: Record<string, string>, argv: Record<string, unknown>) => import("webpack").Configuration} */
module.exports = function (_env = {}, argv = {}) { module.exports = function (_env = {}, argv = {}) {
const mode = argv.mode || "development"; const mode = argv.mode || "development";
const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule]; const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule, javascriptRule];
const envVars = { const envVars = {
GIT_SHA: gitSha, GIT_SHA: gitSha,
PORT: process.env.PORT || "1234", PORT: process.env.PORT || "1234",