Compare commits

..

1 Commits

Author SHA1 Message Date
Asier Isayas
f304600eca legacy mongo shell debug 2025-05-28 12:24:26 -04:00
94 changed files with 1195 additions and 3194 deletions

File diff suppressed because one or more lines are too long

View File

@@ -2869,7 +2869,6 @@ a:link {
z-index: 1000; z-index: 1000;
overflow-y: auto; overflow-y: auto;
overflow-x: clip; overflow-x: clip;
min-height: fit-content;
} }
.uniqueIndexesContainer { .uniqueIndexesContainer {

211
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.3.0", "@azure/cosmos": "4.2.0-beta.1",
"@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,71 +290,59 @@
"version": "2.6.2", "version": "2.6.2",
"license": "0BSD" "license": "0BSD"
}, },
"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-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",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"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": {
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"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/@azure/core-rest-pipeline": {
"version": "1.20.0", "version": "1.18.0",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.20.0.tgz", "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz",
"integrity": "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g==", "integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==",
"dependencies": { "dependencies": {
"@azure/abort-controller": "^2.0.0", "@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.8.0", "@azure/core-auth": "^1.8.0",
"@azure/core-tracing": "^1.0.1", "@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.11.0", "@azure/core-util": "^1.11.0",
"@azure/logger": "^1.0.0", "@azure/logger": "^1.0.0",
"@typespec/ts-http-runtime": "^0.2.2", "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": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"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-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==",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@azure/core-rest-pipeline/node_modules/tslib": { "node_modules/@azure/core-rest-pipeline/node_modules/tslib": {
"version": "2.6.2", "version": "2.6.2",
"license": "0BSD" "license": "0BSD"
@@ -391,16 +379,15 @@
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/@azure/cosmos": { "node_modules/@azure/cosmos": {
"version": "4.3.0", "version": "4.2.0-beta.1",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz", "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz",
"integrity": "sha512-0Ls3l1uWBBSphx6YRhnM+w7rSvq8qVugBCdO6kSiNuRYXEf6+YWLjbzz4e7L2kkz/6ScFdZIOJYP+XtkiRYOhA==", "integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==",
"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",
@@ -505,66 +492,14 @@
"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/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": ">=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": { "node_modules/@azure/logger": {
"version": "1.2.0", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.2.0.tgz", "license": "MIT",
"integrity": "sha512-0hKEzLhpw+ZTAfNJyRrn6s+V0nDWzXk9OjBr2TiGIu0OfMr5s2V4FpKLTAK3Ca5r5OKLbf4hkOGDPyiRjie/jA==",
"dependencies": { "dependencies": {
"@typespec/ts-http-runtime": "^0.2.2", "tslib": "^2.2.0"
"tslib": "^2.6.2"
}, },
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/@azure/logger/node_modules/tslib": { "node_modules/@azure/logger/node_modules/tslib": {
@@ -13139,56 +13074,6 @@
"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.3.0", "@azure/cosmos": "4.2.0-beta.1",
"@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.3.0", "@azure/cosmos": "4.2.0-beta.1",
"@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.3.0", "version": "4.2.0-beta.1",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz", "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.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,3 +1,4 @@
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";
@@ -13,14 +14,18 @@ interface QueryResponse {
} }
export interface MinimalQueryIterator { export interface MinimalQueryIterator {
fetchNext: () => Promise<QueryResponse>; fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>;
} }
// Pick<QueryIterator<any>, "fetchNext">; // Pick<QueryIterator<any>, "fetchNext">;
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> { export function nextPage(
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
queryOperationOptions?: QueryOperationOptions,
): Promise<QueryResults> {
TelemetryProcessor.traceStart(Action.ExecuteQuery); TelemetryProcessor.traceStart(Action.ExecuteQuery);
return documentsIterator.fetchNext().then((response) => { return documentsIterator.fetchNext(queryOperationOptions).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,4 +1,5 @@
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",
@@ -102,9 +103,20 @@ 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

@@ -4,18 +4,13 @@ import * as React from "react";
export interface TooltipProps { export interface TooltipProps {
children: string; children: string;
className?: string; className?: string;
ariaLabelForTooltip?: string;
} }
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children, className }: TooltipProps) => {
children,
className,
ariaLabelForTooltip = children,
}: TooltipProps) => {
return ( return (
<span className={className}> <span className={className}>
<TooltipHost content={children}> <TooltipHost content={children}>
<Icon iconName="Info" aria-label={ariaLabelForTooltip} className="panelInfoIcon" tabIndex={0} /> <Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
</TooltipHost> </TooltipHost>
</span> </span>
); );

View File

@@ -3,7 +3,6 @@
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,
@@ -15,7 +14,6 @@ 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

@@ -42,7 +42,6 @@ export interface IBulkDeleteResult {
export const deleteDocuments = async ( export const deleteDocuments = async (
collection: CollectionBase, collection: CollectionBase,
documentIds: DocumentId[], documentIds: DocumentId[],
abortSignal: AbortSignal,
): Promise<IBulkDeleteResult[]> => { ): Promise<IBulkDeleteResult[]> => {
const clearMessage = logConsoleProgress(`Deleting ${documentIds.length} ${getEntityName(true)}`); const clearMessage = logConsoleProgress(`Deleting ${documentIds.length} ${getEntityName(true)}`);
try { try {
@@ -66,16 +65,12 @@ export const deleteDocuments = async (
operationType: BulkOperationType.Delete, operationType: BulkOperationType.Delete,
})); }));
const promise = v2Container.items const promise = v2Container.items.bulk(operations).then((bulkResults) => {
.bulk(operations, undefined, { return bulkResults.map((bulkResult, index) => {
abortSignal, const documentId = documentIdsChunk[index];
}) return { ...bulkResult, documentId };
.then((bulkResults) => {
return bulkResults.map((bulkResult, index) => {
const documentId = documentIdsChunk[index];
return { ...bulkResult, documentId };
});
}); });
});
promiseArray.push(promise); promiseArray.push(promise);
} }

View File

@@ -26,7 +26,6 @@ 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,3 +1,4 @@
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";
@@ -8,12 +9,13 @@ 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); const result: QueryResults = await nextPage(documentsIterator, firstItemIndex, queryOperationOptions);
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

@@ -126,5 +126,12 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
throw new Error(`Unsupported default experience type: ${apiType}`); throw new Error(`Unsupported default experience type: ${apiType}`);
} }
return rpResponse?.value?.map((collection) => collection.properties?.resource as DataModels.Collection); // TO DO: Remove when we get RP API Spec with materializedViews
/* eslint-disable @typescript-eslint/no-explicit-any */
return rpResponse?.value?.map((collection: any) => {
const collectionDataModel: DataModels.Collection = collection.properties?.resource as DataModels.Collection;
collectionDataModel.materializedViews = collection.properties?.resource?.materializedViews;
collectionDataModel.materializedViewDefinition = collection.properties?.resource?.materializedViewDefinition;
return collectionDataModel;
});
} }

View File

@@ -187,7 +187,8 @@ if (process.env.NODE_ENV === "development") {
PROXY_PATH: "/proxy", PROXY_PATH: "/proxy",
EMULATOR_ENDPOINT: "https://localhost:8081", EMULATOR_ENDPOINT: "https://localhost:8081",
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac, PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac, // MONGO_PROXY_ENDPOINT: "https://cosmos-db-portal-mongoproxy1-mpac-westus.azurewebsites.net",
MONGO_PROXY_ENDPOINT: "https://localhost:7238",
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac, CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac,
}); });
} }

View File

@@ -23,7 +23,6 @@ export enum PaneKind {
GlobalSettings, GlobalSettings,
AdHocAccess, AdHocAccess,
SwitchDirectory, SwitchDirectory,
QuickStart,
} }
/** /**

View File

@@ -7,7 +7,6 @@ export interface ArmEntity {
type: string; type: string;
kind: string; kind: string;
tags?: Tags; tags?: Tags;
resourceGroup?: string;
} }
export interface DatabaseAccount extends ArmEntity { export interface DatabaseAccount extends ArmEntity {
@@ -389,7 +388,7 @@ export interface VectorEmbeddingPolicy {
} }
export interface VectorEmbedding { export interface VectorEmbedding {
dataType: "float32" | "uint8" | "int8"; dataType: "float16" | "float32" | "uint8" | "int8";
dimensions: number; dimensions: number;
distanceFunction: "euclidean" | "cosine" | "dotproduct"; distanceFunction: "euclidean" | "cosine" | "dotproduct";
path: string; path: string;

View File

@@ -1,5 +1,4 @@
import { import {
ItemDefinition,
JSONObject, JSONObject,
QueryMetrics, QueryMetrics,
Resource, Resource,
@@ -31,11 +30,8 @@ export interface UploadDetailsRecord {
numFailed: number; numFailed: number;
numThrottled: number; numThrottled: number;
errors: string[]; errors: string[];
resources?: ItemDefinition[];
} }
export type BulkInsertResult = Omit<UploadDetailsRecord, "fileName">;
export interface QueryResultsMetadata { export interface QueryResultsMetadata {
hasMoreResults: boolean; hasMoreResults: boolean;
firstItemIndex: number; firstItemIndex: number;
@@ -50,7 +46,6 @@ 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

@@ -1,6 +1,7 @@
import { AuthType } from "AuthType"; import { AuthType } from "AuthType";
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import ko from "knockout"; import ko from "knockout";
import { Features } from "Platform/Hosted/extractFeatures";
import React from "react"; import React from "react";
import { updateCollection } from "../../../Common/dataAccess/updateCollection"; import { updateCollection } from "../../../Common/dataAccess/updateCollection";
import { updateOffer } from "../../../Common/dataAccess/updateOffer"; import { updateOffer } from "../../../Common/dataAccess/updateOffer";
@@ -252,7 +253,7 @@ describe("SettingsComponent", () => {
it("should save throughput bucket changes when Save button is clicked", async () => { it("should save throughput bucket changes when Save button is clicked", async () => {
updateUserContext({ updateUserContext({
apiType: "SQL", apiType: "SQL",
throughputBucketsEnabled: true, features: { enableThroughputBuckets: true } as Features,
authType: AuthType.AAD, authType: AuthType.AAD,
}); });

View File

@@ -13,7 +13,7 @@ import {
} from "Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputBucketsComponent"; } from "Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputBucketsComponent";
import { useDatabases } from "Explorer/useDatabases"; import { useDatabases } from "Explorer/useDatabases";
import { isFabricNative } from "Platform/Fabric/FabricUtil"; import { isFabricNative } from "Platform/Fabric/FabricUtil";
import { isVectorSearchEnabled } from "Utils/CapabilityUtils"; import { isFullTextSearchEnabled, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isRunningOnPublicCloud } from "Utils/CloudUtils"; import { isRunningOnPublicCloud } from "Utils/CloudUtils";
import * as React from "react"; import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg"; import DiscardIcon from "../../../../images/discard.svg";
@@ -188,10 +188,13 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.isGlobalSecondaryIndex = this.isGlobalSecondaryIndex =
!!this.collection?.materializedViewDefinition() || !!this.collection?.materializedViews(); !!this.collection?.materializedViewDefinition() || !!this.collection?.materializedViews();
this.isVectorSearchEnabled = isVectorSearchEnabled() && !hasDatabaseSharedThroughput(this.collection); this.isVectorSearchEnabled = isVectorSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
this.isFullTextSearchEnabled = userContext.apiType === "SQL"; this.isFullTextSearchEnabled = isFullTextSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy; this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
this.throughputBucketsEnabled = userContext.throughputBucketsEnabled; this.throughputBucketsEnabled =
userContext.apiType === "SQL" &&
userContext.features.enableThroughputBuckets &&
userContext.authType === AuthType.AAD;
// Mongo container with system partition key still treat as "Fixed" // Mongo container with system partition key still treat as "Fixed"
this.isFixedContainer = this.isFixedContainer =
@@ -1071,11 +1074,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
databaseId: this.collection.databaseId, databaseId: this.collection.databaseId,
collectionId: this.collection.id(), collectionId: this.collection.id(),
currentOffer: this.collection.offer(), currentOffer: this.collection.offer(),
autopilotThroughput: this.collection.offer?.()?.autoscaleMaxThroughput autopilotThroughput: this.collection.offer().autoscaleMaxThroughput
? this.collection.offer?.()?.autoscaleMaxThroughput ? this.collection.offer().autoscaleMaxThroughput
: undefined, : undefined,
manualThroughput: this.collection.offer?.()?.manualThroughput manualThroughput: this.collection.offer().manualThroughput
? this.collection.offer?.()?.manualThroughput ? this.collection.offer().manualThroughput
: undefined, : undefined,
throughputBuckets: this.state.throughputBuckets, throughputBuckets: this.state.throughputBuckets,
}); });
@@ -1091,7 +1094,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
currentOffer: this.collection.offer(), currentOffer: this.collection.offer(),
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined, autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput, manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
throughputBuckets: this.throughputBucketsEnabled ? this.state.throughputBuckets : undefined,
}; };
if (this.hasProvisioningTypeChanged()) { if (this.hasProvisioningTypeChanged()) {
if (this.state.isAutoPilotSelected) { if (this.state.isAutoPilotSelected) {
@@ -1342,7 +1344,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
}); });
} }
if (this.throughputBucketsEnabled && !hasDatabaseSharedThroughput(this.collection) && this.offer) { if (this.throughputBucketsEnabled) {
tabs.push({ tabs.push({
tab: SettingsV2TabTypes.ThroughputBucketsTab, tab: SettingsV2TabTypes.ThroughputBucketsTab,
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />, content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,

View File

@@ -26,7 +26,7 @@ describe("ThroughputBucketsComponent", () => {
it("renders the correct number of buckets", () => { it("renders the correct number of buckets", () => {
render(<ThroughputBucketsComponent {...defaultProps} />); render(<ThroughputBucketsComponent {...defaultProps} />);
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5); expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
}); });
it("renders buckets in the correct order even if input is unordered", () => { it("renders buckets in the correct order even if input is unordered", () => {
@@ -36,14 +36,8 @@ describe("ThroughputBucketsComponent", () => {
]; ];
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />); render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />);
const bucketLabels = screen.getAllByText(/Bucket \d+/).map((el) => el.textContent); const bucketLabels = screen.getAllByText(/Group \d+/).map((el) => el.textContent);
expect(bucketLabels).toEqual([ expect(bucketLabels).toEqual(["Group 1 (Data Explorer Query Bucket)", "Group 2", "Group 3", "Group 4", "Group 5"]);
"Bucket 1 (Data Explorer Query Bucket)",
"Bucket 2",
"Bucket 3",
"Bucket 4",
"Bucket 5",
]);
}); });
it("renders all provided buckets even if they exceed the max default bucket count", () => { it("renders all provided buckets even if they exceed the max default bucket count", () => {
@@ -59,7 +53,7 @@ describe("ThroughputBucketsComponent", () => {
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={oversizedBuckets} />); render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={oversizedBuckets} />);
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(7); expect(screen.getAllByText(/Group \d+/)).toHaveLength(7);
expect(screen.getByDisplayValue("50")).toBeInTheDocument(); expect(screen.getByDisplayValue("50")).toBeInTheDocument();
expect(screen.getByDisplayValue("60")).toBeInTheDocument(); expect(screen.getByDisplayValue("60")).toBeInTheDocument();
@@ -177,7 +171,7 @@ describe("ThroughputBucketsComponent", () => {
it("ensures default buckets are used when no buckets are provided", () => { it("ensures default buckets are used when no buckets are provided", () => {
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />); render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />);
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5); expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
expect(screen.getAllByDisplayValue("100")).toHaveLength(5); expect(screen.getAllByDisplayValue("100")).toHaveLength(5);
}); });
}); });

View File

@@ -76,7 +76,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
value={bucket.maxThroughputPercentage} value={bucket.maxThroughputPercentage}
onChange={(newValue) => handleBucketChange(bucket.id, newValue)} onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
showValue={false} showValue={false}
label={`Bucket ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`} label={`Group ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
styles={{ root: { flex: 2, maxWidth: 400 } }} styles={{ root: { flex: 2, maxWidth: 400 } }}
disabled={bucket.maxThroughputPercentage === 100} disabled={bucket.maxThroughputPercentage === 100}
/> />

View File

@@ -285,7 +285,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
serverId, serverId,
numberOfRegions, numberOfRegions,
isMultimaster, isMultimaster,
false, true,
); );
return ( return (
<div> <div>

View File

@@ -198,32 +198,6 @@ exports[`SettingsComponent renders 1`] = `
timeToLiveSecondsBaseline={5} timeToLiveSecondsBaseline={5}
/> />
</PivotItem> </PivotItem>
<PivotItem
headerText="Container Policies"
itemKey="ContainerVectorPolicyTab"
key="ContainerVectorPolicyTab"
style={
{
"marginTop": 20,
}
}
>
<ContainerPolicyComponent
fullTextPolicy={{}}
fullTextPolicyBaseline={{}}
isFullTextSearchEnabled={true}
isGlobalSecondaryIndex={true}
isVectorSearchEnabled={false}
onFullTextPolicyChange={[Function]}
onFullTextPolicyDirtyChange={[Function]}
onVectorEmbeddingPolicyChange={[Function]}
onVectorEmbeddingPolicyDirtyChange={[Function]}
resetShouldDiscardContainerPolicyChange={[Function]}
shouldDiscardContainerPolicies={false}
vectorEmbeddingPolicy={{}}
vectorEmbeddingPolicyBaseline={{}}
/>
</PivotItem>
<PivotItem <PivotItem
headerText="Indexing Policy" headerText="Indexing Policy"
itemKey="IndexingPolicyTab" itemKey="IndexingPolicyTab"

View File

@@ -1,4 +1,4 @@
import { Stack, Text } from "@fluentui/react"; import { Text } from "@fluentui/react";
import React, { FunctionComponent } from "react"; import React, { FunctionComponent } from "react";
import { InfoTooltip } from "../../../../Common/Tooltip/InfoTooltip"; import { InfoTooltip } from "../../../../Common/Tooltip/InfoTooltip";
import * as SharedConstants from "../../../../Shared/Constants"; import * as SharedConstants from "../../../../Shared/Constants";
@@ -44,42 +44,33 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
const currencySign: string = getCurrencySign(serverId); const currencySign: string = getCurrencySign(serverId);
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled); const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier); const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
const estimatedMonthlyCost = "Estimated monthly cost";
const iconWithEstimatedCostDisclaimer: JSX.Element = ( const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>{estimatedCostDisclaimer}</InfoTooltip>;
<InfoTooltip ariaLabelForTooltip={`${estimatedMonthlyCost} ${currency} ${estimatedCostDisclaimer}`}>
{estimatedCostDisclaimer}
</InfoTooltip>
);
if (isAutoscale) { if (isAutoscale) {
return ( return (
<Stack style={{ marginBottom: 6 }}> <Text variant="small">
<Text variant="small"> Estimated monthly cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
{estimatedMonthlyCost} ({currency}){iconWithEstimatedCostDisclaimer}:{" "} <b>
<b> {currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "}
{currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "} {currencySign + calculateEstimateNumber(monthlyPrice)}{" "}
{currencySign + calculateEstimateNumber(monthlyPrice)}{" "} </b>
</b> ({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits / 10} - {requestUnits}{" "}
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits / 10} - {requestUnits}{" "} RU/s, {currencySign + pricePerRu}/RU)
RU/s, {currencySign + pricePerRu}/RU) </Text>
</Text>
</Stack>
); );
} }
return ( return (
<Stack style={{ marginBottom: 8 }}> <Text variant="small">
<Text variant="small"> Estimated cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
Estimated cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "} <b>
<b> {currencySign + calculateEstimateNumber(hourlyPrice)} hourly /{" "}
{currencySign + calculateEstimateNumber(hourlyPrice)} hourly /{" "} {currencySign + calculateEstimateNumber(dailyPrice)} daily /{" "}
{currencySign + calculateEstimateNumber(dailyPrice)} daily /{" "} {currencySign + calculateEstimateNumber(monthlyPrice)} monthly{" "}
{currencySign + calculateEstimateNumber(monthlyPrice)} monthly{" "} </b>
</b> ({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits}RU/s,{" "}
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits}RU/s,{" "} {currencySign + pricePerRu}/RU)
{currencySign + pricePerRu}/RU) </Text>
</Text>
</Stack>
); );
}; };

View File

@@ -1,6 +1,5 @@
import { Checkbox, DirectionalHint, Link, Separator, Stack, Text, TextField, TooltipHost } from "@fluentui/react"; import { Checkbox, DirectionalHint, Link, Stack, Text, TextField, TooltipHost } from "@fluentui/react";
import { getWorkloadType } from "Common/DatabaseAccountUtility"; import { getWorkloadType } from "Common/DatabaseAccountUtility";
import { CostEstimateText } from "Explorer/Controls/ThroughputInput/CostEstimateText/CostEstimateText";
import { useDatabases } from "Explorer/useDatabases"; import { useDatabases } from "Explorer/useDatabases";
import React, { FunctionComponent, useEffect, useState } from "react"; import React, { FunctionComponent, useEffect, useState } from "react";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
@@ -10,8 +9,8 @@ import { userContext } from "../../../UserContext";
import { getCollectionName } from "../../../Utils/APITypeUtils"; import { getCollectionName } from "../../../Utils/APITypeUtils";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import * as PricingUtils from "../../../Utils/PricingUtils"; import * as PricingUtils from "../../../Utils/PricingUtils";
import { CostEstimateText } from "./CostEstimateText/CostEstimateText";
import "./ThroughputInput.less"; import "./ThroughputInput.less";
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
export interface ThroughputInputProps { export interface ThroughputInputProps {
isDatabase: boolean; isDatabase: boolean;
@@ -44,8 +43,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
if ( if (
isFreeTier || isFreeTier ||
isQuickstart || isQuickstart ||
[Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(workloadType) || [Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(workloadType)
isFabricNative()
) { ) {
defaultThroughput = AutoPilotUtils.autoPilotThroughput1K; defaultThroughput = AutoPilotUtils.autoPilotThroughput1K;
} else if (workloadType === Constants.WorkloadType.Production) { } else if (workloadType === Constants.WorkloadType.Production) {
@@ -232,92 +230,53 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
</div> </div>
</Stack> </Stack>
)} )}
{isAutoscaleSelected && ( {isAutoscaleSelected && (
<Stack className="throughputInputSpacing"> <Stack className="throughputInputSpacing">
<Text style={{ marginTop: -2, fontSize: 12 }}> <Text variant="small" aria-label="capacity calculator of azure cosmos db">
Your container throughput will automatically scale up to the maximum value you select, from a minimum of 10% Estimate your required RU/s with{" "}
of that value. <Link
</Text> className="underlinedLink outlineNone"
<Stack horizontal verticalAlign="end" tokens={{ childrenGap: 8 }}> target="_blank"
<Stack tokens={{ childrenGap: 4 }}> href="https://cosmos.azure.com/capacitycalculator/"
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}> aria-label="capacity calculator of azure cosmos db"
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
Minimum RU/s
</Text>
<InfoTooltip>The minimum RU/s your container will scale to</InfoTooltip>
</Stack>
<Text
style={{
fontFamily: "Segoe UI",
width: 70,
height: 27,
border: "none",
fontSize: 14,
backgroundColor: "transparent",
fontWeight: 400,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{Math.round(throughput / 10).toString()}
</Text>
</Stack>
<Text
style={{
fontFamily: "Segoe UI",
fontSize: 12,
fontWeight: 400,
paddingBottom: 6,
}}
> >
x 10 = capacity calculator
</Text> </Link>
.
</Text>
<Stack tokens={{ childrenGap: 4 }}> <Stack horizontal>
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}> <Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }} aria-label="maxRUDescription">
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}> {isDatabase ? "Database" : getCollectionName()} Max RU/s
Maximum RU/s </Text>
</Text> <InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
</Stack>
<TextField
id="autoscaleRUValueField"
type="number"
styles={{
fieldGroup: { width: 100, height: 27, flexShrink: 0 },
field: { fontSize: 14, fontWeight: 400 },
}}
onChange={(_event, newInput?: string) => onThroughputValueChange(newInput)}
step={AutoPilotUtils.autoPilotIncrementStep}
min={AutoPilotUtils.autoPilotThroughput1K}
max={isSharded ? Number.MAX_SAFE_INTEGER.toString() : "10000"}
value={throughput.toString()}
ariaLabel={`${isDatabase ? "Database" : getCollectionName()} max RU/s`}
required={true}
errorMessage={throughputError}
/>
</Stack>
</Stack> </Stack>
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} /> <TextField
<Stack className="throughputInputSpacing"> id="autoscaleRUValueField"
<Text variant="small" aria-label="ruDescription"> type="number"
Estimate your required RU/s with&nbsp; styles={{
<Link fieldGroup: { width: 300, height: 27 },
className="underlinedLink" field: { fontSize: 12 },
target="_blank" }}
href="https://cosmos.azure.com/capacitycalculator/" onChange={(event, newInput?: string) => onThroughputValueChange(newInput)}
aria-label="Capacity calculator" step={AutoPilotUtils.autoPilotIncrementStep}
> min={AutoPilotUtils.autoPilotThroughput1K}
capacity calculator max={isSharded ? Number.MAX_SAFE_INTEGER.toString() : "10000"}
</Link> value={throughput.toString()}
. ariaLabel={`${isDatabase ? "Database" : getCollectionName()} max RU/s`}
</Text> required={true}
</Stack> errorMessage={throughputError}
<Separator className="panelSeparator" style={{ paddingTop: -8, paddingBottom: -8 }} /> />
<Text variant="small">
Your {isDatabase ? "database" : getCollectionName().toLocaleLowerCase()} throughput will automatically scale
from{" "}
<b>
{AutoPilotUtils.getMinRUsBasedOnUserInput(throughput)} RU/s (10% of max RU/s) - {throughput} RU/s
</b>{" "}
based on usage.
</Text>
</Stack> </Stack>
)} )}
@@ -341,6 +300,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
</Text> </Text>
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip> <InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
</Stack> </Stack>
<TooltipHost <TooltipHost
directionalHint={DirectionalHint.topLeftEdge} directionalHint={DirectionalHint.topLeftEdge}
content={ content={
@@ -365,10 +325,11 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
errorMessage={throughputError} errorMessage={throughputError}
/> />
</TooltipHost> </TooltipHost>
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
</Stack> </Stack>
)} )}
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
{throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && ( {throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && (
<Stack horizontal verticalAlign="start"> <Stack horizontal verticalAlign="start">
<Checkbox <Checkbox

View File

@@ -12,7 +12,6 @@ import { isFabricMirrored, isFabricMirroredKey, scheduleRefreshFabricToken } fro
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils"; import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils"; import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts"; import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout"; import * as ko from "knockout";
@@ -31,7 +30,6 @@ import { readDatabases } from "../Common/dataAccess/readDatabases";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse } from "../Contracts/DataModels"; import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse } from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import { UploadDetailsRecord } from "../Contracts/ViewModels";
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService"; import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
import { PhoenixClient } from "../Phoenix/PhoenixClient"; import { PhoenixClient } from "../Phoenix/PhoenixClient";
import * as ExplorerSettings from "../Shared/ExplorerSettings"; import * as ExplorerSettings from "../Shared/ExplorerSettings";
@@ -285,12 +283,43 @@ export default class Explorer {
} }
public openInVsCode(): void { public openInVsCode(): void {
TelemetryProcessor.traceStart(Action.OpenVSCode);
this.openVsCodeButtonClick();
}
private openVsCodeButtonClick(): void {
const activeTab = useTabs.getState().activeTab; const activeTab = useTabs.getState().activeTab;
const resourceId = encodeURIComponent(userContext.databaseAccount.id); const resourceId = encodeURIComponent(userContext.databaseAccount.id);
const database = encodeURIComponent(activeTab?.collection?.databaseId); const database = encodeURIComponent(activeTab?.collection?.databaseId);
const container = encodeURIComponent(activeTab?.collection?.id()); const container = encodeURIComponent(activeTab?.collection?.id());
const baseUrl = `vscode://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`; const baseUrl = `vscod://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl; const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
const startTime = Date.now();
let vsCodeNotOpened = false;
setTimeout(() => {
const timeOutTime = Date.now() - startTime;
if (!vsCodeNotOpened && timeOutTime < 1050) {
vsCodeNotOpened = true;
useDialog.getState().openDialog(openVSCodeDialogProps);
}
}, 1000);
const link = document.createElement("a");
link.href = vscodeUrl;
link.rel = "noopener noreferrer";
document.body.appendChild(link);
try {
link.click();
document.body.removeChild(link);
TelemetryProcessor.traceStart(Action.OpenVSCode);
} catch (error) {
if (!vsCodeNotOpened) {
vsCodeNotOpened = true;
logConsoleError(`Failed to open VS Code: ${getErrorMessage(error)}`);
}
}
const openVSCodeDialogProps: DialogProps = { const openVSCodeDialogProps: DialogProps = {
linkProps: { linkProps: {
@@ -305,19 +334,15 @@ export default class Explorer {
secondaryButtonText: "Cancel", secondaryButtonText: "Cancel",
onPrimaryButtonClick: () => { onPrimaryButtonClick: () => {
try { vsCodeNotOpened = false;
window.location.href = vscodeUrl; this.openVsCodeButtonClick();
TelemetryProcessor.traceStart(Action.OpenVSCode); useDialog.getState().closeDialog();
} catch (error) {
logConsoleError(`Failed to open VS Code: ${getErrorMessage(error)}`);
}
}, },
onSecondaryButtonClick: () => { onSecondaryButtonClick: () => {
useDialog.getState().closeDialog(); useDialog.getState().closeDialog();
TelemetryProcessor.traceCancel(Action.OpenVSCode); TelemetryProcessor.traceCancel(Action.OpenVSCode);
}, },
}; };
useDialog.getState().openDialog(openVSCodeDialogProps);
} }
public async openCESCVAFeedbackBlade(): Promise<void> { public async openCESCVAFeedbackBlade(): Promise<void> {
@@ -1116,8 +1141,8 @@ export default class Explorer {
} }
} }
public openUploadItemsPane(onUpload?: (data: UploadDetailsRecord[]) => void): void { public openUploadItemsPane(): void {
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane onUpload={onUpload} />); useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
} }
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void { public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
useSidePanel useSidePanel
@@ -1125,7 +1150,7 @@ export default class Explorer {
.openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />); .openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />);
} }
public getDownloadModalContent(fileName: string): JSX.Element { public getDownloadModalConent(fileName: string): JSX.Element {
if (useNotebook.getState().isPhoenixNotebooks) { if (useNotebook.getState().isPhoenixNotebooks) {
return ( return (
<> <>
@@ -1171,11 +1196,6 @@ export default class Explorer {
await this.initNotebooks(userContext.databaseAccount); await this.initNotebooks(userContext.databaseAccount);
} }
if (userContext.authType === AuthType.AAD && userContext.apiType === "SQL") {
const throughputBucketsEnabled = await featureRegistered(userContext.subscriptionId, "ThroughputBucketing");
updateUserContext({ throughputBucketsEnabled });
}
this.refreshSampleData(); this.refreshSampleData();
} }

View File

@@ -16,12 +16,7 @@ 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 { import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
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";
@@ -1088,7 +1083,6 @@ 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) {
@@ -1105,8 +1099,6 @@ 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

@@ -95,10 +95,3 @@
white-space: nowrap; white-space: nowrap;
} }
} }
@media (max-width: 768px) {
.newVertexComponent {
padding: 0;
width: 100%;
}
}

View File

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

View File

@@ -173,20 +173,8 @@
.message { .message {
flex-grow: 1; flex-grow: 1;
white-space:pre-wrap; white-space:pre-wrap;
overflow-wrap: break-word;
word-break: break-word;
} }
} }
} }
} }
@media (max-width: 768px) {
.notificationConsoleContents {
overflow-y: auto;
.notificationConsoleData {
overflow: visible;
}
}
}
} }

View File

@@ -14,7 +14,6 @@ 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";
@@ -92,9 +91,6 @@ 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">
@@ -122,10 +118,6 @@ 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" />
@@ -206,7 +198,6 @@ 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,19 +59,6 @@ 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"
@@ -242,19 +229,6 @@ 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

@@ -188,11 +188,6 @@ function openPane(action: ActionContracts.OpenPane, explorer: Explorer) {
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.AddCollection] action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.AddCollection]
) { ) {
explorer.onNewCollectionClicked(); explorer.onNewCollectionClicked();
} else if (
action.paneKind === ActionContracts.PaneKind.QuickStart ||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.QuickStart]
) {
explorer.onNewCollectionClicked({ isQuickstart: true });
} else if ( } else if (
action.paneKind === ActionContracts.PaneKind.CassandraAddCollection || action.paneKind === ActionContracts.PaneKind.CassandraAddCollection ||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.CassandraAddCollection] action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.CassandraAddCollection]

View File

@@ -25,7 +25,7 @@ import { FullTextPoliciesComponent } from "Explorer/Controls/FullTextSeach/FullT
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent"; import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
import { import {
AllPropertiesIndexed, AllPropertiesIndexed,
AnalyticalStoreHeader, AnalyticalStorageContent,
ContainerVectorPolicyTooltipContent, ContainerVectorPolicyTooltipContent,
FullTextPolicyDefault, FullTextPolicyDefault,
getPartitionKey, getPartitionKey,
@@ -49,7 +49,12 @@ import { Action } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { getCollectionName } from "Utils/APITypeUtils"; import { getCollectionName } from "Utils/APITypeUtils";
import { isCapabilityEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils"; import {
isCapabilityEnabled,
isFullTextSearchEnabled,
isServerlessAccount,
isVectorSearchEnabled,
} from "Utils/CapabilityUtils";
import { getUpsellMessage } from "Utils/PricingUtils"; import { getUpsellMessage } from "Utils/PricingUtils";
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils"; import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
import { CollapsibleSectionComponent } from "../../Controls/CollapsiblePanel/CollapsibleSectionComponent"; import { CollapsibleSectionComponent } from "../../Controls/CollapsiblePanel/CollapsibleSectionComponent";
@@ -60,7 +65,6 @@ import { useDatabases } from "../../useDatabases";
import { PanelFooterComponent } from "../PanelFooterComponent"; import { PanelFooterComponent } from "../PanelFooterComponent";
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent"; import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
import { PanelLoadingScreen } from "../PanelLoadingScreen"; import { PanelLoadingScreen } from "../PanelLoadingScreen";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
export interface AddCollectionPanelProps { export interface AddCollectionPanelProps {
explorer: Explorer; explorer: Explorer;
@@ -106,7 +110,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
private collectionThroughput: number; private collectionThroughput: number;
private isCollectionAutoscale: boolean; private isCollectionAutoscale: boolean;
private isCostAcknowledged: boolean; private isCostAcknowledged: boolean;
private showFullTextSearch: boolean;
constructor(props: AddCollectionPanelProps) { constructor(props: AddCollectionPanelProps) {
super(props); super(props);
@@ -141,8 +144,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
fullTextIndexes: [], fullTextIndexes: [],
fullTextPolicyValidated: true, fullTextPolicyValidated: true,
}; };
this.showFullTextSearch = userContext.apiType === "SQL";
} }
componentDidMount(): void { componentDidMount(): void {
@@ -265,7 +266,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<div className="panelMainContent"> <div className="panelMainContent">
{!(isFabricNative() && this.props.databaseId !== undefined) && ( {!(isFabricNative() && this.props.databaseId !== undefined) && (
<Stack hidden={userContext.apiType === "Tables"} style={{ marginBottom: -2 }}> <Stack hidden={userContext.apiType === "Tables"}>
<Stack horizontal> <Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span> <span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
@@ -336,6 +337,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
size={40} size={40}
className="panelTextField" className="panelTextField"
aria-label="New database id, Type a new database id" aria-label="New database id, Type a new database id"
autoFocus
tabIndex={0} tabIndex={0}
value={this.state.newDatabaseId} value={this.state.newDatabaseId}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
@@ -406,12 +408,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
responsiveMode={999} responsiveMode={999}
/> />
)} )}
<Separator className="panelSeparator" />
</Stack> </Stack>
)} )}
<Separator className="panelSeparator" style={{ marginTop: -4, marginBottom: -4 }} />
<Stack> <Stack>
<Stack horizontal style={{ marginTop: -5, marginBottom: 1 }}> <Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span> <span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
{`${getCollectionName()} id`} {`${getCollectionName()} id`}
@@ -449,10 +451,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
} }
/> />
</Stack> </Stack>
<Separator className="panelSeparator" style={{ marginTop: -5, marginBottom: -5 }} />
{this.shouldShowIndexingOptionsForFreeTierAccount() && ( {this.shouldShowIndexingOptionsForFreeTierAccount() && (
<Stack> <Stack>
<Stack horizontal style={{ marginTop: -4, marginBottom: -5 }}> <Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span> <span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
Indexing Indexing
@@ -498,7 +500,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
(!this.state.isSharedThroughputChecked || (!this.state.isSharedThroughputChecked ||
this.props.explorer.isFixedCollectionWithSharedThroughputSupported()) && ( this.props.explorer.isFixedCollectionWithSharedThroughputSupported()) && (
<Stack> <Stack>
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}> <Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span> <span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
Sharding Sharding
@@ -554,7 +556,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{this.state.isSharded && ( {this.state.isSharded && (
<Stack> <Stack>
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}> <Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span> <span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
{getPartitionKeyName()} {getPartitionKeyName()}
@@ -598,7 +600,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{userContext.apiType === "SQL" && {userContext.apiType === "SQL" &&
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => { this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
return ( return (
<Stack style={{ marginBottom: 2, marginTop: -5 }} key={`uniqueKey${index}`} horizontal> <Stack style={{ marginBottom: 8 }} key={`uniqueKey${index}`} horizontal>
<div <div
style={{ style={{
width: "20px", width: "20px",
@@ -666,7 +668,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
)} )}
</Stack> </Stack>
)} )}
<Separator className="panelSeparator" style={{ marginTop: 2, marginBottom: -4 }} />
</Stack> </Stack>
)} )}
@@ -727,7 +728,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
)} )}
{!isFabricNative() && userContext.apiType === "SQL" && ( {!isFabricNative() && userContext.apiType === "SQL" && (
<Stack style={{ marginTop: -2, marginBottom: -4 }}> <Stack>
{UniqueKeysHeader()} {UniqueKeysHeader()}
{this.state.uniqueKeys.map((uniqueKey: string, i: number): JSX.Element => { {this.state.uniqueKeys.map((uniqueKey: string, i: number): JSX.Element => {
return ( return (
@@ -741,6 +742,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
: "Comma separated paths e.g. /firstName,/address/zipCode" : "Comma separated paths e.g. /firstName,/address/zipCode"
} }
className="panelTextField" className="panelTextField"
autoFocus
value={uniqueKey} value={uniqueKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => { const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => {
@@ -775,12 +777,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Stack> </Stack>
)} )}
<Separator className="panelSeparator" style={{ marginTop: -15, marginBottom: -4 }} />
{shouldShowAnalyticalStoreOptions() && ( {shouldShowAnalyticalStoreOptions() && (
<Stack className="panelGroupSpacing" style={{ marginTop: -4 }}> <Stack className="panelGroupSpacing">
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
{AnalyticalStoreHeader()} {AnalyticalStorageContent()}
</Text> </Text>
<Stack horizontal verticalAlign="center"> <Stack horizontal verticalAlign="center">
@@ -821,7 +821,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<Stack className="panelGroupSpacing"> <Stack className="panelGroupSpacing">
<Text variant="small"> <Text variant="small">
Azure Synapse Link is required for creating an analytical store{" "} Azure Synapse Link is required for creating an analytical store{" "}
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account. <br /> {getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account.{" "}
<Link <Link
href="https://aka.ms/cosmosdb-synapselink" href="https://aka.ms/cosmosdb-synapselink"
target="_blank" target="_blank"
@@ -1161,7 +1161,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
} }
private shouldShowFullTextSearchParameters() { private shouldShowFullTextSearchParameters() {
return !isFabricNative() && this.showFullTextSearch; return isFullTextSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
} }
private parseUniqueKeys(): DataModels.UniqueKeyPolicy { private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
@@ -1316,7 +1316,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}; };
} }
if (this.showFullTextSearch) { if (this.shouldShowFullTextSearchParameters()) {
indexingPolicy.fullTextIndexes = this.state.fullTextIndexes; indexingPolicy.fullTextIndexes = this.state.fullTextIndexes;
} }
@@ -1350,12 +1350,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
let offerThroughput: number; let offerThroughput: number;
let autoPilotMaxThroughput: number; let autoPilotMaxThroughput: number;
// Throughput if (databaseLevelThroughput) {
if (isFabricNative()) {
// Fabric Native accounts are always autoscale and have a fixed throughput of 1K
autoPilotMaxThroughput = AutoPilotUtils.autoPilotThroughput1K;
offerThroughput = undefined;
} else if (databaseLevelThroughput) {
if (this.state.createNewDatabase) { if (this.state.createNewDatabase) {
if (this.isNewDatabaseAutoscale) { if (this.isNewDatabaseAutoscale) {
autoPilotMaxThroughput = this.newDatabaseThroughput; autoPilotMaxThroughput = this.newDatabaseThroughput;

View File

@@ -73,7 +73,7 @@ export function UniqueKeysHeader(): JSX.Element {
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."; "Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key.";
return ( return (
<Stack horizontal style={{ marginBottom: -2 }}> <Stack horizontal>
<Text className="panelTextBold" variant="small"> <Text className="panelTextBold" variant="small">
Unique keys Unique keys
</Text> </Text>
@@ -98,21 +98,6 @@ export function shouldShowAnalyticalStoreOptions(): boolean {
} }
} }
export function AnalyticalStoreHeader(): JSX.Element {
const tooltipContent =
"Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.";
return (
<Stack horizontal style={{ marginBottom: -2 }}>
<Text className="panelTextBold" variant="small">
Analytical Store
</Text>
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={tooltipContent}>
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} ariaLabel={tooltipContent} />
</TooltipHost>
</Stack>
);
}
export function AnalyticalStorageContent(): JSX.Element { export function AnalyticalStorageContent(): JSX.Element {
return ( return (
<Text variant="small"> <Text variant="small">

View File

@@ -11,11 +11,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
> >
<Stack <Stack
hidden={false} hidden={false}
style={
{
"marginBottom": -2,
}
}
> >
<Stack <Stack
horizontal={true} horizontal={true}
@@ -93,6 +88,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
aria-label="New database id, Type a new database id" aria-label="New database id, Type a new database id"
aria-required={true} aria-required={true}
autoComplete="off" autoComplete="off"
autoFocus={true}
className="panelTextField" className="panelTextField"
id="newDatabaseId" id="newDatabaseId"
name="newDatabaseId" name="newDatabaseId"
@@ -142,25 +138,13 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
</StyledTooltipHostBase> </StyledTooltipHostBase>
</Stack> </Stack>
</Stack> </Stack>
<Separator
className="panelSeparator"
/>
</Stack> </Stack>
<Separator
className="panelSeparator"
style={
{
"marginBottom": -4,
"marginTop": -4,
}
}
/>
<Stack> <Stack>
<Stack <Stack
horizontal={true} horizontal={true}
style={
{
"marginBottom": 1,
"marginTop": -5,
}
}
> >
<span <span
className="mandatoryStar" className="mandatoryStar"
@@ -203,24 +187,9 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
value="" value=""
/> />
</Stack> </Stack>
<Separator
className="panelSeparator"
style={
{
"marginBottom": -5,
"marginTop": -5,
}
}
/>
<Stack> <Stack>
<Stack <Stack
horizontal={true} horizontal={true}
style={
{
"marginBottom": -4,
"marginTop": -5,
}
}
> >
<span <span
className="mandatoryStar" className="mandatoryStar"
@@ -285,15 +254,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
Add hierarchical partition key Add hierarchical partition key
</CustomizedDefaultButton> </CustomizedDefaultButton>
</Stack> </Stack>
<Separator
className="panelSeparator"
style={
{
"marginBottom": -4,
"marginTop": 2,
}
}
/>
</Stack> </Stack>
<ThroughputInput <ThroughputInput
isDatabase={false} isDatabase={false}
@@ -303,21 +263,9 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
setIsThroughputCapExceeded={[Function]} setIsThroughputCapExceeded={[Function]}
setThroughputValue={[Function]} setThroughputValue={[Function]}
/> />
<Stack <Stack>
style={
{
"marginBottom": -4,
"marginTop": -2,
}
}
>
<Stack <Stack
horizontal={true} horizontal={true}
style={
{
"marginBottom": -2,
}
}
> >
<Text <Text
className="panelTextBold" className="panelTextBold"
@@ -358,53 +306,26 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
Add unique key Add unique key
</CustomizedActionButton> </CustomizedActionButton>
</Stack> </Stack>
<Separator
className="panelSeparator"
style={
{
"marginBottom": -4,
"marginTop": -15,
}
}
/>
<Stack <Stack
className="panelGroupSpacing" className="panelGroupSpacing"
style={
{
"marginTop": -4,
}
}
> >
<Text <Text
className="panelTextBold" className="panelTextBold"
variant="small" variant="small"
> >
<Stack <Text
horizontal={true} variant="small"
style={
{
"marginBottom": -2,
}
}
> >
<Text Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.
className="panelTextBold"
variant="small" <StyledLinkBase
aria-label="Learn more about analytical store."
href="https://aka.ms/analytical-store-overview"
target="_blank"
> >
Analytical Store Learn more
</Text> </StyledLinkBase>
<StyledTooltipHostBase </Text>
content="Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads."
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> </Text>
<Stack <Stack
horizontal={true} horizontal={true}
@@ -461,7 +382,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
container container
. Enable Synapse Link for this Cosmos DB account. . Enable Synapse Link for this Cosmos DB account.
<br />
<StyledLinkBase <StyledLinkBase
aria-label="Learn more about Azure Synapse Link." aria-label="Learn more about Azure Synapse Link."
className="capacitycalculator-link" className="capacitycalculator-link"
@@ -490,44 +411,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
/> />
</Stack> </Stack>
</Stack> </Stack>
<Stack>
<CollapsibleSectionComponent
isExpandedByDefault={false}
onExpand={[Function]}
title="Container Full Text Search Policy"
>
<Stack
id="collapsibleFullTextPolicySectionContent"
styles={
{
"root": {
"position": "relative",
},
}
}
>
<Stack
styles={
{
"root": {
"paddingLeft": 40,
},
}
}
>
<FullTextPoliciesComponent
fullTextPolicy={
{
"defaultLanguage": "en-US",
"fullTextPaths": [],
}
}
onFullTextPathChange={[Function]}
/>
</Stack>
</Stack>
</CollapsibleSectionComponent>
</Stack>
<CollapsibleSectionComponent <CollapsibleSectionComponent
isExpandedByDefault={false} isExpandedByDefault={false}
onExpand={[Function]} onExpand={[Function]}

View File

@@ -40,12 +40,12 @@ import { PanelInfoErrorComponent } from "Explorer/Panes/PanelInfoErrorComponent"
import { PanelLoadingScreen } from "Explorer/Panes/PanelLoadingScreen"; import { PanelLoadingScreen } from "Explorer/Panes/PanelLoadingScreen";
import { useDatabases } from "Explorer/useDatabases"; import { useDatabases } from "Explorer/useDatabases";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import React, { useEffect, useState } from "react";
import { CollectionCreation } from "Shared/Constants"; import { CollectionCreation } from "Shared/Constants";
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 { userContext } from "UserContext"; import { userContext } from "UserContext";
import { isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils"; import { isFullTextSearchEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils"; import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
export interface AddGlobalSecondaryIndexPanelProps { export interface AddGlobalSecondaryIndexPanelProps {
@@ -75,8 +75,6 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
const [showErrorDetails, setShowErrorDetails] = useState<boolean>(); const [showErrorDetails, setShowErrorDetails] = useState<boolean>();
const [isExecuting, setIsExecuting] = useState<boolean>(); const [isExecuting, setIsExecuting] = useState<boolean>();
const showFullTextSearch: MutableRefObject<boolean> = useRef<boolean>(userContext.apiType === "SQL");
useEffect(() => { useEffect(() => {
const sourceContainerOptions: IDropdownOption[] = []; const sourceContainerOptions: IDropdownOption[] = [];
useDatabases.getState().databases.forEach((database: Database) => { useDatabases.getState().databases.forEach((database: Database) => {
@@ -142,6 +140,10 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
return isVectorSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput()); return isVectorSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput());
}; };
const showFullTextSearchParameters = (): boolean => {
return isFullTextSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput());
};
const getAnalyticalStorageTtl = (): number => { const getAnalyticalStorageTtl = (): number => {
if (!isSynapseLinkEnabled()) { if (!isSynapseLinkEnabled()) {
return undefined; return undefined;
@@ -226,7 +228,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
}; };
} }
if (showFullTextSearch) { if (showFullTextSearchParameters()) {
indexingPolicy.fullTextIndexes = fullTextIndexes; indexingPolicy.fullTextIndexes = fullTextIndexes;
} }
@@ -385,7 +387,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
}} }}
/> />
)} )}
{showFullTextSearch && ( {showFullTextSearchParameters() && (
<FullTextSearchComponent <FullTextSearchComponent
{...{ fullTextPolicy, setFullTextPolicy, setFullTextIndexes, setFullTextPolicyValidated }} {...{ fullTextPolicy, setFullTextPolicy, setFullTextIndexes, setFullTextPolicyValidated }}
/> />

View File

@@ -172,17 +172,6 @@ exports[`AddGlobalSecondaryIndexPanel render default panel 1`] = `
} }
setEnableAnalyticalStore={[Function]} setEnableAnalyticalStore={[Function]}
/> />
<FullTextSearchComponent
fullTextPolicy={
{
"defaultLanguage": "en-US",
"fullTextPaths": [],
}
}
setFullTextIndexes={[Function]}
setFullTextPolicy={[Function]}
setFullTextPolicyValidated={[Function]}
/>
<AdvancedComponent <AdvancedComponent
setSubPartitionKeys={[Function]} setSubPartitionKeys={[Function]}
setUseHashV1={[Function]} setUseHashV1={[Function]}

View File

@@ -11,10 +11,10 @@
margin: 20px 0; margin: 20px 0;
overflow-x: hidden; overflow-x: hidden;
&> :not(.collapsibleSection) { & > :not(.collapsibleSection) {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;
&> :not(:last-child) { & > :not(:last-child) {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;
} }
} }
@@ -56,14 +56,6 @@
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
} }
@media (max-width: 768px) {
.panelMainContent {
padding: 0 24px;
margin: 0;
overflow-x: auto;
}
}
} }
.panelHeader { .panelHeader {
@@ -121,87 +113,70 @@
.deleteCollectionFeedback { .deleteCollectionFeedback {
margin-top: 12px; margin-top: 12px;
} }
.addRemoveIcon { .addRemoveIcon {
margin-left: 4px !important; margin-left: 4px !important;
} }
.addRemoveIconLabel { .addRemoveIconLabel {
margin-top: 28px; margin-top: 28px;
margin-left: 4px !important; margin-left: 4px !important;
} }
.addRemoveIcon [alt="editEntity"]:focus, .addRemoveIcon [alt="editEntity"]:focus,
.addRemoveIconLabel [alt="editEntity"]:focus { .addRemoveIconLabel [alt="editEntity"]:focus {
border: 1px dashed #605e5c; border: 1px dashed #605e5c;
} }
.addNewParamStyle { .addNewParamStyle {
margin-top: 5px; margin-top: 5px;
margin-left: 5px !important; margin-left: 5px !important;
cursor: pointer; cursor: pointer;
} }
.panelGroupSpacing> :not(:last-child) { .panelGroupSpacing > :not(:last-child) {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;
} }
.fileUpload { .fileUpload {
display: none !important; display: none !important;
} }
.customFileUpload { .customFileUpload {
padding: 25px 0px 0px 10px; padding: 25px 0px 0px 10px;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
} }
.fileIcon { .fileIcon {
align-self: center; align-self: center;
} }
.panelAddIconLabel { .panelAddIconLabel {
font-size: 20px; font-size: 20px;
width: 20px; width: 20px;
margin: 30px 0 0 10px; margin: 30px 0 0 10px;
cursor: default; cursor: default;
} }
.panelAddIcon { .panelAddIcon {
font-size: 20px; font-size: 20px;
width: 20px; width: 20px;
margin: 30px 0 0 10px; margin: 30px 0 0 10px;
cursor: default; cursor: default;
} }
.removeIcon { .removeIcon {
color: @InfoIconColor; color: @InfoIconColor;
} }
.backImageIcon { .backImageIcon {
margin-top: 8px; margin-top: 8px;
} }
[alt="back"]:focus { [alt="back"]:focus {
border: 1px solid #605e5c; border: 1px solid #605e5c;
} }
.addEntityDatePicker { .addEntityDatePicker {
max-width: 145px; max-width: 145px;
} }
.addEntityTextField { .addEntityTextField {
width: 237px; width: 237px;
} }
.addButtonEntiy { .addButtonEntiy {
width: 25%; width: 25%;
} }
.column-select-view { .column-select-view {
margin: 20px 0px 0px 0px; margin: 20px 0px 0px 0px;
} }
.panelSeparator::before { .panelSeparator::before {
background-color: #edebe9; background-color: #edebe9;
} }

View File

@@ -180,11 +180,6 @@ 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)
@@ -209,7 +204,6 @@ 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) &&
@@ -387,7 +381,6 @@ 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());
@@ -417,7 +410,6 @@ 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,
@@ -768,6 +760,7 @@ 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>
@@ -950,38 +943,6 @@ 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,51 +495,6 @@ 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

@@ -48,11 +48,7 @@ const classNames = mergeStyleSets({
warning: [{ color: theme.semanticColors.warningIcon }, iconClass], warning: [{ color: theme.semanticColors.warningIcon }, iconClass],
}); });
export type UploadItemsPaneProps = { export const UploadItemsPane: FunctionComponent = () => {
onUpload?: (data: UploadDetailsRecord[]) => void;
};
export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpload }) => {
const [files, setFiles] = useState<FileList>(); const [files, setFiles] = useState<FileList>();
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]); const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
const [formError, setFormError] = useState<string>(""); const [formError, setFormError] = useState<string>("");
@@ -75,8 +71,6 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpl
(uploadDetails) => { (uploadDetails) => {
setUploadFileData(uploadDetails.data); setUploadFileData(uploadDetails.data);
setFiles(undefined); setFiles(undefined);
// Emit the upload details to the parent component
onUpload && onUpload(uploadDetails.data);
}, },
(error: Error) => { (error: Error) => {
const errorMessage = getErrorMessage(error); const errorMessage = getErrorMessage(error);

View File

@@ -30,10 +30,8 @@ import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboard
import { isFabric, isFabricMirrored, isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil"; import { isFabric, isFabricMirrored, isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils"; import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
import { conditionalClass } from "Utils/StyleUtils";
import { Allotment, AllotmentHandle } from "allotment"; import { Allotment, AllotmentHandle } from "allotment";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import useZoomLevel from "hooks/useZoomLevel";
import { debounce } from "lodash"; import { debounce } from "lodash";
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
@@ -106,23 +104,6 @@ const useSidebarStyles = makeStyles({
display: "flex", display: "flex",
}, },
}, },
accessibleContent: {
"@media (max-width: 420px)": {
overflow: "scroll",
},
},
minHeightResponsive: {
"@media (max-width: 420px)": {
minHeight: "400px",
},
},
accessibleContentZoom: {
overflow: "scroll",
},
minHeightZoom: {
minHeight: "400px",
},
}); });
interface GlobalCommandsProps { interface GlobalCommandsProps {
@@ -294,7 +275,6 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
const [expandedSize, setExpandedSize] = React.useState(300); const [expandedSize, setExpandedSize] = React.useState(300);
const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo"; const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo";
const allotment = useRef<AllotmentHandle>(null); const allotment = useRef<AllotmentHandle>(null);
const isZoomed = useZoomLevel();
const expand = useCallback(() => { const expand = useCallback(() => {
if (!expanded) { if (!expanded) {
@@ -345,23 +325,11 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
return ( return (
<div className="sidebarContainer"> <div className="sidebarContainer">
<Allotment <Allotment ref={allotment} onChange={onChange} onDragEnd={onDragEnd} className="resourceTreeAndTabs">
ref={allotment}
onChange={onChange}
onDragEnd={onDragEnd}
className={`resourceTreeAndTabs ${styles.accessibleContent} ${conditionalClass(
isZoomed,
styles.accessibleContentZoom,
)}`}
>
{/* Collections Tree - Start */} {/* Collections Tree - Start */}
{hasSidebar && ( {hasSidebar && (
// When collapsed, we force the pane to 24 pixels wide and make it non-resizable. // When collapsed, we force the pane to 24 pixels wide and make it non-resizable.
<Allotment.Pane <Allotment.Pane minSize={24} preferredSize={250}>
className={`${styles.minHeightResponsive} ${conditionalClass(isZoomed, styles.minHeightZoom)}`}
minSize={24}
preferredSize={250}
>
<CosmosFluentProvider className={mergeClasses(styles.sidebar)}> <CosmosFluentProvider className={mergeClasses(styles.sidebar)}>
<div className={styles.sidebarContainer}> <div className={styles.sidebarContainer}>
{loading && ( {loading && (
@@ -417,10 +385,7 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
</CosmosFluentProvider> </CosmosFluentProvider>
</Allotment.Pane> </Allotment.Pane>
)} )}
<Allotment.Pane <Allotment.Pane minSize={200}>
className={`${styles.minHeightResponsive} ${conditionalClass(isZoomed, styles.minHeightZoom)}`}
minSize={200}
>
<Tabs explorer={explorer} /> <Tabs explorer={explorer} />
</Allotment.Pane> </Allotment.Pane>
</Allotment> </Allotment>

View File

@@ -30,21 +30,6 @@
margin: 0px auto; margin: 0px auto;
text-align: center; text-align: center;
} }
.splashStackContainer {
.splashStackRow {
display: flex;
gap: 0 16px;
@media (max-width: 768px) {
flex-direction: column;
gap: 16px 0;
}
}
@media (max-width: 768px) {
width: 85% !important;
}
}
.mainButtonsContainer { .mainButtonsContainer {
.flex-display(); .flex-display();

View File

@@ -28,7 +28,6 @@ import LinkIcon from "../../../images/Link_blue.svg";
import PowerShellIcon from "../../../images/PowerShell.svg"; import PowerShellIcon from "../../../images/PowerShell.svg";
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg"; import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
import QuickStartIcon from "../../../images/Quickstart_Lightning.svg"; import QuickStartIcon from "../../../images/Quickstart_Lightning.svg";
import VisualStudioIcon from "../../../images/VisualStudio.svg";
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg"; import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
import CollectionIcon from "../../../images/tree-collection.svg"; import CollectionIcon from "../../../images/tree-collection.svg";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
@@ -126,12 +125,8 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
useDatabases.getState().sampleDataResourceTokenCollection useDatabases.getState().sampleDataResourceTokenCollection
) { ) {
return ( return (
<Stack <Stack style={{ width: "66%", cursor: "pointer", margin: "40px auto" }} tokens={{ childrenGap: 16 }}>
className="splashStackContainer" <Stack horizontal tokens={{ childrenGap: 16 }}>
style={{ width: "66%", cursor: "pointer", margin: "40px auto" }}
tokens={{ childrenGap: 16 }}
>
<Stack className="splashStackRow" horizontal>
<SplashScreenButton <SplashScreenButton
imgSrc={QuickStartIcon} imgSrc={QuickStartIcon}
title={"Launch quick start"} title={"Launch quick start"}
@@ -151,7 +146,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
}} }}
/> />
</Stack> </Stack>
<Stack className="splashStackRow" horizontal> <Stack horizontal tokens={{ childrenGap: 16 }}>
{useQueryCopilot.getState().copilotEnabled && ( {useQueryCopilot.getState().copilotEnabled && (
<SplashScreenButton <SplashScreenButton
imgSrc={CopilotIcon} imgSrc={CopilotIcon}
@@ -295,10 +290,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
<form className="connectExplorerFormContainer"> <form className="connectExplorerFormContainer">
<div className="splashScreenContainer"> <div className="splashScreenContainer">
<div className="splashScreen"> <div className="splashScreen">
<h2 className="title" role="heading" aria-label={title}> <h1 className="title" role="heading" aria-label={title}>
{title} {title}
<FeaturePanelLauncher /> <FeaturePanelLauncher />
</h2> </h1>
<div className="subtitle">{subtitle}</div> <div className="subtitle">{subtitle}</div>
{this.getSplashScreenButtons()} {this.getSplashScreenButtons()}
{useCarousel.getState().showCoachMark && ( {useCarousel.getState().showCoachMark && (
@@ -463,10 +458,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
} }
if (userContext.apiType === "VCoreMongo") { if (userContext.apiType === "VCoreMongo") {
icon = VisualStudioIcon; icon = ContainersIcon;
title = "Connect with VS Code"; title = "Connect with Studio 3T";
description = "Query and Manage your MongoDB cluster in Visual Studio Code"; description = "Prefer Studio 3T? Find your connection strings here";
onClick = () => this.container.openInVsCode(); onClick = () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect);
} }
return { return {

View File

@@ -26,6 +26,7 @@ import { useDialog } from "Explorer/Controls/Dialog";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { InputDataList, InputDatalistDropdownOptionSection } from "Explorer/Controls/InputDataList/InputDataList"; import { InputDataList, InputDatalistDropdownOptionSection } from "Explorer/Controls/InputDataList/InputDataList";
import { ProgressModalDialog } from "Explorer/Controls/ProgressModalDialog"; import { ProgressModalDialog } from "Explorer/Controls/ProgressModalDialog";
import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities"; import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import { import {
@@ -63,7 +64,7 @@ import * as Logger from "../../../Common/Logger";
import * as MongoProxyClient from "../../../Common/MongoProxyClient"; import * as MongoProxyClient from "../../../Common/MongoProxyClient";
import * as DataModels from "../../../Contracts/DataModels"; import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import { CollectionBase, UploadDetailsRecord } from "../../../Contracts/ViewModels"; import { CollectionBase } from "../../../Contracts/ViewModels";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as QueryUtils from "../../../Utils/QueryUtils"; import * as QueryUtils from "../../../Utils/QueryUtils";
import { defaultQueryFields, extractPartitionKeyValues } from "../../../Utils/QueryUtils"; import { defaultQueryFields, extractPartitionKeyValues } from "../../../Utils/QueryUtils";
@@ -143,13 +144,6 @@ export const useDocumentsTabStyles = makeStyles({
deleteProgressContent: { deleteProgressContent: {
paddingTop: tokens.spacingVerticalL, paddingTop: tokens.spacingVerticalL,
}, },
smallScreenContent: {
"@media (max-width: 420px)": {
flexWrap: "wrap",
minHeight: "max-content",
padding: "4px",
},
},
}); });
export class DocumentsTabV2 extends TabsBase { export class DocumentsTabV2 extends TabsBase {
@@ -308,6 +302,7 @@ type UiKeyboardEvent = (e: KeyboardEvent | React.SyntheticEvent<Element, Event>)
// Export to expose to unit tests // Export to expose to unit tests
export type ButtonsDependencies = { export type ButtonsDependencies = {
_collection: ViewModels.CollectionBase;
selectedRows: Set<TableRowId>; selectedRows: Set<TableRowId>;
editorState: ViewModels.DocumentExplorerState; editorState: ViewModels.DocumentExplorerState;
isPreferredApiMongoDB: boolean; isPreferredApiMongoDB: boolean;
@@ -318,7 +313,26 @@ export type ButtonsDependencies = {
onSaveExistingDocumentClick: UiKeyboardEvent; onSaveExistingDocumentClick: UiKeyboardEvent;
onRevertExistingDocumentClick: UiKeyboardEvent; onRevertExistingDocumentClick: UiKeyboardEvent;
onDeleteExistingDocumentsClick: UiKeyboardEvent; onDeleteExistingDocumentsClick: UiKeyboardEvent;
onUploadDocumentsClick: UiKeyboardEvent; };
const createUploadButton = (container: Explorer): CommandButtonComponentProps => {
const label = "Upload Item";
return {
id: UPLOAD_BUTTON_ID,
iconSrc: UploadIcon,
iconAlt: label,
onCommandClick: () => {
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
selectedCollection && container.openUploadItemsPane();
},
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled:
useSelectedNode.getState().isDatabaseNodeOrNoneSelected() ||
!useClientWriteEnabled.getState().clientWriteEnabled ||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
};
}; };
// Export to expose to unit tests // Export to expose to unit tests
@@ -331,6 +345,7 @@ export const UPLOAD_BUTTON_ID = "uploadItemBtn";
// Export to expose in unit tests // Export to expose in unit tests
export const getTabsButtons = ({ export const getTabsButtons = ({
_collection,
selectedRows, selectedRows,
editorState, editorState,
isPreferredApiMongoDB, isPreferredApiMongoDB,
@@ -341,7 +356,6 @@ export const getTabsButtons = ({
onSaveExistingDocumentClick, onSaveExistingDocumentClick,
onRevertExistingDocumentClick, onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick, onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
}: ButtonsDependencies): CommandButtonComponentProps[] => { }: ButtonsDependencies): CommandButtonComponentProps[] => {
if (isFabric() && userContext.fabricContext?.isReadOnly) { if (isFabric() && userContext.fabricContext?.isReadOnly) {
// All the following buttons require write access // All the following buttons require write access
@@ -453,20 +467,7 @@ export const getTabsButtons = ({
} }
if (!isPreferredApiMongoDB) { if (!isPreferredApiMongoDB) {
const label = "Upload Item"; buttons.push(createUploadButton(_collection.container));
buttons.push({
id: UPLOAD_BUTTON_ID,
iconSrc: UploadIcon,
iconAlt: label,
onCommandClick: onUploadDocumentsClick,
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled:
useSelectedNode.getState().isDatabaseNodeOrNoneSelected() ||
!useClientWriteEnabled.getState().clientWriteEnabled ||
useSelectedNode.getState().isQueryCopilotCollectionSelected(),
});
} }
return buttons; return buttons;
@@ -671,7 +672,6 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
collection: CollectionBase; collection: CollectionBase;
}>(undefined); }>(undefined);
const [bulkDeleteMode, setBulkDeleteMode] = useState<"inProgress" | "completed" | "aborting" | "aborted">(undefined); const [bulkDeleteMode, setBulkDeleteMode] = useState<"inProgress" | "completed" | "aborting" | "aborted">(undefined);
const [abortController, setAbortController] = useState<AbortController | undefined>(undefined);
const setKeyboardActions = useKeyboardActionGroup(KeyboardActionGroup.ACTIVE_TAB); const setKeyboardActions = useKeyboardActionGroup(KeyboardActionGroup.ACTIVE_TAB);
@@ -699,19 +699,13 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
return; return;
} }
if (bulkDeleteProcess.pendingIds.length === 0 && bulkDeleteProcess.throttledIds.length === 0) { if (
// Successfully deleted all documents (bulkDeleteProcess.pendingIds.length === 0 && bulkDeleteProcess.throttledIds.length === 0) ||
bulkDeleteMode === "aborting"
) {
// Successfully deleted all documents or operation was aborted
bulkDeleteOperation.onCompleted(bulkDeleteProcess.successfulIds); bulkDeleteOperation.onCompleted(bulkDeleteProcess.successfulIds);
setBulkDeleteMode("completed"); setBulkDeleteMode(bulkDeleteMode === "aborting" ? "aborted" : "completed");
return;
}
if (bulkDeleteMode === "aborting") {
// Operation was aborted
abortController?.abort();
bulkDeleteOperation.onCompleted(bulkDeleteProcess.successfulIds);
setBulkDeleteMode("aborted");
setAbortController(undefined);
return; return;
} }
@@ -719,10 +713,8 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const newPendingIds = bulkDeleteProcess.pendingIds.concat(bulkDeleteProcess.throttledIds); const newPendingIds = bulkDeleteProcess.pendingIds.concat(bulkDeleteProcess.throttledIds);
const timeout = bulkDeleteProcess.beforeExecuteMs || 0; const timeout = bulkDeleteProcess.beforeExecuteMs || 0;
const ac = new AbortController();
setAbortController(ac);
setTimeout(() => { setTimeout(() => {
deleteNoSqlDocuments(bulkDeleteOperation.collection, [...newPendingIds], ac.signal) deleteNoSqlDocuments(bulkDeleteOperation.collection, [...newPendingIds])
.then((deleteResult) => { .then((deleteResult) => {
let retryAfterMilliseconds = 0; let retryAfterMilliseconds = 0;
const newSuccessful: DocumentId[] = []; const newSuccessful: DocumentId[] = [];
@@ -878,6 +870,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
} }
updateNavbarWithTabsButtons(isTabActive, { updateNavbarWithTabsButtons(isTabActive, {
_collection,
selectedRows, selectedRows,
editorState, editorState,
isPreferredApiMongoDB, isPreferredApiMongoDB,
@@ -888,7 +881,6 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onSaveExistingDocumentClick, onSaveExistingDocumentClick,
onRevertExistingDocumentClick, onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick, onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
}); });
}, []); }, []);
@@ -1294,47 +1286,11 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
); );
}, [deleteDocuments, documentIds, isPreferredApiMongoDB, selectedRows]); }, [deleteDocuments, documentIds, isPreferredApiMongoDB, selectedRows]);
const onUploadDocumentsClick = useCallback((): void => {
if (!isPreferredApiMongoDB) {
const onSuccessUpload = (data: UploadDetailsRecord[]) => {
const addedIdsSet = new Set(
data
.reduce(
(result: ItemDefinition[], record) =>
result.concat(record.resources && record.resources.length ? record.resources : []),
[],
)
.map((document) => {
const partitionKeyValueArray: PartitionKey[] = extractPartitionKeyValues(
document,
partitionKey as PartitionKeyDefinition,
);
return newDocumentId(
document as ItemDefinition & Resource,
partitionKeyProperties,
partitionKeyValueArray as string[],
);
}),
);
const documents = new Set(documentIds);
addedIdsSet.forEach((item) => documents.add(item));
setDocumentIds(Array.from(documents));
setSelectedDocumentContent(undefined);
setClickedRowIndex(undefined);
setSelectedRows(new Set());
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
};
_collection.container.openUploadItemsPane(onSuccessUpload);
}
}, [_collection.container, documentIds, isPreferredApiMongoDB, newDocumentId, partitionKey, partitionKeyProperties]);
// If editor state changes, update the nav // If editor state changes, update the nav
useEffect( useEffect(
() => () =>
updateNavbarWithTabsButtons(isTabActive, { updateNavbarWithTabsButtons(isTabActive, {
_collection,
selectedRows, selectedRows,
editorState, editorState,
isPreferredApiMongoDB, isPreferredApiMongoDB,
@@ -1343,11 +1299,11 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onSaveNewDocumentClick, onSaveNewDocumentClick,
onRevertNewDocumentClick, onRevertNewDocumentClick,
onSaveExistingDocumentClick, onSaveExistingDocumentClick,
onRevertExistingDocumentClick, onRevertExistingDocumentClick: onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick, onDeleteExistingDocumentsClick: onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
}), }),
[ [
_collection,
selectedRows, selectedRows,
editorState, editorState,
isPreferredApiMongoDB, isPreferredApiMongoDB,
@@ -1358,7 +1314,6 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onSaveExistingDocumentClick, onSaveExistingDocumentClick,
onRevertExistingDocumentClick, onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick, onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
isTabActive, isTabActive,
], ],
); );
@@ -2147,7 +2102,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
return ( return (
<CosmosFluentProvider className={styles.container}> <CosmosFluentProvider className={styles.container}>
<div data-test={"DocumentsTab"} className="tab-pane active" role="tabpanel" style={{ display: "flex" }}> <div data-test={"DocumentsTab"} className="tab-pane active" role="tabpanel" style={{ display: "flex" }}>
<div data-test={"DocumentsTab/Filter"} className={`${styles.filterRow} ${styles.smallScreenContent}`}> <div data-test={"DocumentsTab/Filter"} className={styles.filterRow}>
{!isPreferredApiMongoDB && <span> SELECT * FROM c </span>} {!isPreferredApiMongoDB && <span> SELECT * FROM c </span>}
<InputDataList <InputDataList
dropdownOptions={getFilterChoices()} dropdownOptions={getFilterChoices()}

View File

@@ -15,7 +15,7 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
} }
> >
<div <div
className="___11ktxfv_0000000 f1o614cb fy9rknc f22iagw fsnqrgy f1f5gg8d fjodcmx f122n59 f1f09k3d fg706s2 frpde29 ___1ngl8o6_0000000 fz7mnu6 fl3egqs flhmrkm" className="___11ktxfv_0000000 f1o614cb fy9rknc f22iagw fsnqrgy f1f5gg8d fjodcmx f122n59 f1f09k3d fg706s2 frpde29"
data-test="DocumentsTab/Filter" data-test="DocumentsTab/Filter"
> >
<span> <span>

View File

@@ -128,7 +128,7 @@ export default class MongoShellTabComponent extends Component<
apiEndpoint: apiEndpoint, apiEndpoint: apiEndpoint,
}, },
}, },
window.origin, "https://localhost:443",
); );
} }

View File

@@ -7,5 +7,5 @@ export function getMongoShellUrl(): string {
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint; const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`; const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
return `/mongoshell/index.html?${queryString}`; return `https://localhost:443/index.html?${queryString}`;
} }

View File

@@ -8,8 +8,6 @@ import RunQuery from "../../../../images/RunQuery.png";
import { QueryResults } from "../../../Contracts/ViewModels"; import { QueryResults } from "../../../Contracts/ViewModels";
import { ErrorList } from "./ErrorList"; import { ErrorList } from "./ErrorList";
import { ResultsView } from "./ResultsView"; import { ResultsView } from "./ResultsView";
import useZoomLevel from "hooks/useZoomLevel";
import { conditionalClass } from "Utils/StyleUtils";
export interface ResultsViewProps { export interface ResultsViewProps {
isMongoDB: boolean; isMongoDB: boolean;
@@ -25,16 +23,11 @@ interface QueryResultProps extends ResultsViewProps {
const ExecuteQueryCallToAction: React.FC = () => { const ExecuteQueryCallToAction: React.FC = () => {
const styles = useQueryTabStyles(); const styles = useQueryTabStyles();
const isZoomed = useZoomLevel();
return ( return (
<div data-test="QueryTab/ResultsPane/ExecuteCTA" className={styles.executeCallToAction}> <div data-test="QueryTab/ResultsPane/ExecuteCTA" className={styles.executeCallToAction}>
<div> <div>
<p> <p>
<img <img src={RunQuery} aria-hidden="true" />
className={`${styles.responsiveImg} ${conditionalClass(isZoomed, styles.zoomedImageSize)}`}
src={RunQuery}
aria-hidden="true"
/>
</p> </p>
<p>Execute a query to see the results</p> <p>Execute a query to see the results</p>
</div> </div>

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 } from "@azure/cosmos"; import { FeedOptions, QueryOperationOptions } 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 } from "Shared/StorageUtility"; import { LocalStorageUtility, StorageKey, getRUThreshold, ruThresholdEnabled } 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,9 +369,22 @@ 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(this.props.collection && this.props.collection.id(), this._iterator, firstItemIndex); await queryDocumentsPage(
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,
@@ -411,9 +424,6 @@ 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

@@ -32,7 +32,6 @@ enum ResultsTabs {
const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, executeQueryDocumentsPage }) => { const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, executeQueryDocumentsPage }) => {
const styles = useQueryTabStyles(); const styles = useQueryTabStyles();
/* eslint-disable react/prop-types */
const queryResultsString = queryResults const queryResultsString = queryResults
? isMongoDB ? isMongoDB
? MongoUtility.tojson(queryResults.documents, undefined, false) ? MongoUtility.tojson(queryResults.documents, undefined, false)
@@ -48,172 +47,6 @@ const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, execu
await executeQueryDocumentsPage(firstItemIndex + itemCount - 1); await executeQueryDocumentsPage(firstItemIndex + itemCount - 1);
}; };
const ExportResults: React.FC = () => {
const [showDropdown, setShowDropdown] = useState(false);
const dropdownRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setShowDropdown(false);
}
};
if (showDropdown) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [showDropdown]);
const escapeCsvValue = (value: string): string => {
return `"${value.replace(/"/g, '""')}"`;
};
const formatValueForCsv = (value: string | object): string => {
if (value === null || value === undefined) {
return "";
}
if (typeof value === "object") {
return escapeCsvValue(JSON.stringify(value));
}
return escapeCsvValue(String(value));
};
const exportToCsv = () => {
try {
const allHeadersSet = new Set<string>();
queryResults.documents.forEach((doc) => {
Object.keys(doc).forEach((key) => allHeadersSet.add(key));
});
const allHeaders = Array.from(allHeadersSet);
const csvHeader = allHeaders.map(escapeCsvValue).join(",");
const csvData = queryResults.documents
.map((doc) =>
allHeaders.map((header) => (doc[header] !== undefined ? formatValueForCsv(doc[header]) : "")).join(","),
)
.join("\n");
const csvContent = `sep=,\n${csvHeader}\n${csvData}`;
downloadFile(csvContent, "query-results.csv", "text/csv");
} catch (error) {
console.error("Failed to export CSV:", error);
}
};
const exportToJson = () => {
try {
downloadFile(queryResultsString, "query-results.json", "application/json");
} catch (error) {
console.error("Failed to export JSON:", error);
}
};
const downloadFile = (content: string, fileName: string, contentType: string) => {
const blob = new Blob([content], { type: contentType });
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement("a");
downloadLink.href = url;
downloadLink.download = fileName;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
setTimeout(() => URL.revokeObjectURL(url), 100);
};
const handleExport = (format: "CSV" | "JSON") => {
setShowDropdown(false);
if (format === "CSV") {
exportToCsv();
} else {
exportToJson();
}
};
const handleKeyDown = (e: React.KeyboardEvent, format: "CSV" | "JSON") => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleExport(format);
} else if (e.key === "Escape") {
setShowDropdown(false);
}
};
return (
<div style={{ position: "relative", display: "inline-block" }} ref={dropdownRef}>
<Button
onClick={() => setShowDropdown((v) => !v)}
size="small"
appearance="transparent"
icon={<ArrowDownloadRegular />}
title="Download Query Results"
aria-haspopup="listbox"
aria-expanded={showDropdown}
/>
{showDropdown && (
<div
style={{
position: "absolute",
right: 0,
zIndex: 10,
background: "white",
border: "1px solid #ccc",
borderRadius: 2,
minWidth: 60,
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
marginTop: 4,
}}
role="listbox"
tabIndex={-1}
>
<button
style={{
display: "block",
width: "100%",
padding: "8px 16px",
background: "none",
border: "none",
textAlign: "left",
cursor: "pointer",
transition: "background 0.2s",
}}
onMouseOver={(e) => (e.currentTarget.style.background = "#f3f3f3")}
onMouseOut={(e) => (e.currentTarget.style.background = "none")}
onClick={() => handleExport("JSON")}
onKeyDown={(e) => handleKeyDown(e, "JSON")}
role="option"
tabIndex={0}
>
JSON
</button>
<button
style={{
display: "block",
width: "100%",
padding: "8px 16px",
background: "none",
border: "none",
textAlign: "left",
cursor: "pointer",
transition: "background 0.2s",
}}
onMouseOver={(e) => (e.currentTarget.style.background = "#f3f3f3")}
onMouseOut={(e) => (e.currentTarget.style.background = "none")}
onClick={() => handleExport("CSV")}
onKeyDown={(e) => handleKeyDown(e, "CSV")}
role="option"
tabIndex={0}
>
CSV
</button>
</div>
)}
</div>
);
};
return ( return (
<> <>
<div className={styles.queryResultsBar}> <div className={styles.queryResultsBar}>
@@ -234,7 +67,6 @@ const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, execu
aria-label="Copy" aria-label="Copy"
onClick={onClickCopyResults} onClick={onClickCopyResults}
/> />
<ExportResults />
</div> </div>
<div className={styles.queryResultsViewer}> <div className={styles.queryResultsViewer}>
<EditorReact language={"json"} content={queryResultsString} isReadOnly={true} ariaLabel={"Query results"} /> <EditorReact language={"json"} content={queryResultsString} isReadOnly={true} ariaLabel={"Query results"} />

View File

@@ -25,9 +25,6 @@ export const useQueryTabStyles = makeStyles({
height: "100%", height: "100%",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
"@media (max-width: 420px)": {
overflow: "scroll",
},
}, },
queryResultsMessage: { queryResultsMessage: {
...shorthands.margin("5px"), ...shorthands.margin("5px"),
@@ -41,9 +38,6 @@ export const useQueryTabStyles = makeStyles({
display: "flex", display: "flex",
rowGap: "12px", rowGap: "12px",
flexDirection: "column", flexDirection: "column",
"@media (max-width: 420px)": {
height: "auto",
},
}, },
queryResultsTabContentContainer: { queryResultsTabContentContainer: {
display: "flex", display: "flex",
@@ -99,12 +93,4 @@ export const useQueryTabStyles = makeStyles({
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
}, },
responsiveImg: {
"@media (max-width: 420px)": {
width: "50px",
},
},
zoomedImageSize: {
width: "60px",
},
}); });

View File

@@ -18,7 +18,6 @@ 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";
@@ -118,9 +117,6 @@ 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" />
)} )}
@@ -198,20 +194,6 @@ 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,7 +27,6 @@ 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,7 +21,7 @@ import { readTriggers } from "../../Common/dataAccess/readTriggers";
import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions"; import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { BulkInsertResult, UploadDetailsRecord } from "../../Contracts/ViewModels"; import { UploadDetailsRecord } from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
@@ -1092,13 +1092,17 @@ export default class Collection implements ViewModels.Collection {
}); });
} }
public async bulkInsertDocuments(documents: JSONObject[]): Promise<BulkInsertResult> { public async bulkInsertDocuments(documents: JSONObject[]): Promise<{
const stats: BulkInsertResult = { numSucceeded: number;
numFailed: number;
numThrottled: number;
errors: string[];
}> {
const stats = {
numSucceeded: 0, numSucceeded: 0,
numFailed: 0, numFailed: 0,
numThrottled: 0, numThrottled: 0,
errors: [] as string[], errors: [] as string[],
resources: [],
}; };
const chunkSize = 100; // 100 is the max # of bulk operations the SDK currently accepts const chunkSize = 100; // 100 is the max # of bulk operations the SDK currently accepts
@@ -1116,7 +1120,6 @@ export default class Collection implements ViewModels.Collection {
responses.forEach((response, index) => { responses.forEach((response, index) => {
if (response.statusCode === 201) { if (response.statusCode === 201) {
stats.numSucceeded++; stats.numSucceeded++;
stats.resources.push(response.resourceBody);
} else if (response.statusCode === 429) { } else if (response.statusCode === 429) {
documentsToAttempt.push(attemptedDocuments[index]); documentsToAttempt.push(attemptedDocuments[index]);
} else if (response.statusCode === 409) { } else if (response.statusCode === 409) {
@@ -1149,22 +1152,18 @@ export default class Collection implements ViewModels.Collection {
numFailed: 0, numFailed: 0,
numThrottled: 0, numThrottled: 0,
errors: [], errors: [],
resources: [],
}; };
try { try {
const parsedContent = JSON.parse(documentContent); const parsedContent = JSON.parse(documentContent);
if (Array.isArray(parsedContent)) { if (Array.isArray(parsedContent)) {
const { numSucceeded, numFailed, numThrottled, errors, resources } = const { numSucceeded, numFailed, numThrottled, errors } = await this.bulkInsertDocuments(parsedContent);
await this.bulkInsertDocuments(parsedContent);
record.numSucceeded = numSucceeded; record.numSucceeded = numSucceeded;
record.numFailed = numFailed; record.numFailed = numFailed;
record.numThrottled = numThrottled; record.numThrottled = numThrottled;
record.errors = errors; record.errors = errors;
record.resources = record.resources.concat(resources);
} else { } else {
const resource = await createDocument(this, parsedContent); await createDocument(this, parsedContent);
record.resources.push(resource);
record.numSucceeded++; record.numSucceeded++;
} }

View File

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

View File

@@ -117,7 +117,6 @@ export interface UserContext {
readonly feedbackPolicies?: AdminFeedbackPolicySettings; readonly feedbackPolicies?: AdminFeedbackPolicySettings;
readonly dataPlaneRbacEnabled?: boolean; readonly dataPlaneRbacEnabled?: boolean;
readonly refreshCosmosClient?: boolean; readonly refreshCosmosClient?: boolean;
throughputBucketsEnabled?: boolean;
} }
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo"; export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";

View File

@@ -20,3 +20,7 @@ export const isServerlessAccount = (): boolean => {
export const isVectorSearchEnabled = (): boolean => { export const isVectorSearchEnabled = (): boolean => {
return userContext.apiType === "SQL" && isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLVectorSearch); return userContext.apiType === "SQL" && isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLVectorSearch);
}; };
export const isFullTextSearchEnabled = (): boolean => {
return userContext.apiType === "SQL" && isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLFullTextSearch);
};

View File

@@ -5,6 +5,7 @@ export function validateEndpoint(
endpointToValidate: string | undefined, endpointToValidate: string | undefined,
allowedEndpoints: ReadonlyArray<string>, allowedEndpoints: ReadonlyArray<string>,
): boolean { ): boolean {
return true;
try { try {
return validateEndpointInternal( return validateEndpointInternal(
endpointToValidate, endpointToValidate,

View File

@@ -1,48 +0,0 @@
import { configContext } from "ConfigContext";
import { FeatureRegistration } from "Contracts/DataModels";
import { AuthorizationTokenHeaderMetadata } from "Contracts/ViewModels";
import { getAuthorizationHeader } from "Utils/AuthorizationUtils";
export const featureRegistered = async (subscriptionId: string, feature: string) => {
const api_version = "2021-07-01";
const url = `${configContext.ARM_ENDPOINT}/subscriptions/${subscriptionId}/providers/Microsoft.Features/featureProviders/Microsoft.DocumentDB/subscriptionFeatureRegistrations/${feature}?api-version=${api_version}`;
const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
const headers = { [authorizationHeader.header]: authorizationHeader.token };
let response;
try {
response = await _fetchWithTimeout(url, headers);
} catch (error) {
return false;
}
if (!response?.ok) {
return false;
}
const featureRegistration = (await response?.json()) as FeatureRegistration;
return featureRegistration?.properties?.state === "Registered";
};
async function _fetchWithTimeout(
url: string,
headers: {
[x: string]: string;
},
) {
const timeout = 10000;
const options = { timeout };
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await window.fetch(url, {
headers,
...options,
signal: controller.signal,
});
clearTimeout(id);
return response;
}

View File

@@ -245,7 +245,7 @@ export function downloadItem(
}, },
"Cancel", "Cancel",
undefined, undefined,
container.getDownloadModalContent(name), container.getDownloadModalConent(name),
); );
} }
export async function downloadNotebookItem( export async function downloadNotebookItem(

View File

@@ -5,6 +5,7 @@ export function isInvalidParentFrameOrigin(event: MessageEvent): boolean {
} }
function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEvent): boolean { function isValidOrigin(allowedOrigins: ReadonlyArray<string>, event: MessageEvent): boolean {
return true;
const eventOrigin = (event && event.origin) || ""; const eventOrigin = (event && event.origin) || "";
const windowOrigin = (window && window.origin) || ""; const windowOrigin = (window && window.origin) || "";
if (eventOrigin === windowOrigin) { if (eventOrigin === windowOrigin) {

View File

@@ -23,7 +23,3 @@ 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,7 +1,4 @@
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";
@@ -89,18 +86,6 @@ 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

@@ -21,11 +21,3 @@ export function copyStyles(sourceDoc: Document, targetDoc: Document): void {
} }
}); });
} }
/**
* Conditionally returns a class name based on a boolean condition.
* If the condition is true, returns the `trueValue` class; otherwise, returns `falseValue` (or an empty string if not provided).
*/
export function conditionalClass(condition: boolean, trueValue: string, falseValue?: string): string {
return condition ? trueValue : falseValue || "";
}

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */ /* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
export async function listCassandraKeyspaces( export async function listCassandraKeyspaces(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and collection. */ /* Retrieves the metrics determined by the given filter for the given database account and collection. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */ /* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */ /* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */ /* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and database. */ /* Retrieves the metrics determined by the given filter for the given database account and database. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and region. */ /* Retrieves the metrics determined by the given filter for the given database account and region. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the properties of an existing Azure Cosmos DB database account. */ /* Retrieves the properties of an existing Azure Cosmos DB database account. */
export async function get( export async function get(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists the graphs under an existing Azure Cosmos DB database account. */ /* Lists the graphs under an existing Azure Cosmos DB database account. */
export async function listGraphs( export async function listGraphs(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */ /* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
export async function listGremlinDatabases( export async function listGremlinDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* List Cosmos DB locations and their properties */ /* List Cosmos DB locations and their properties */
export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> { export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> {

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */ /* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
export async function listMongoDBDatabases( export async function listMongoDBDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists all of the available Cosmos DB Resource Provider operations. */ /* Lists all of the available Cosmos DB Resource Provider operations. */
export async function list(): Promise<Types.OperationListResult> { export async function list(): Promise<Types.OperationListResult> {

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given partition key range id. */ /* Retrieves the metrics determined by the given filter for the given partition key range id. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given partition key range id and region. */ /* Retrieves the metrics determined by the given filter for the given partition key range id and region. */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account. This url is only for PBS and Replication Latency data */ /* Retrieves the metrics determined by the given filter for the given database account. This url is only for PBS and Replication Latency data */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given account, source and target region. This url is only for PBS and Replication Latency data */ /* Retrieves the metrics determined by the given filter for the given account, source and target region. This url is only for PBS and Replication Latency data */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Retrieves the metrics determined by the given filter for the given account target region. This url is only for PBS and Replication Latency data */ /* Retrieves the metrics determined by the given filter for the given account target region. This url is only for PBS and Replication Latency data */
export async function listMetrics( export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists the SQL databases under an existing Azure Cosmos DB database account. */ /* Lists the SQL databases under an existing Azure Cosmos DB database account. */
export async function listSqlDatabases( export async function listSqlDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request"; import { armRequest } from "../../request";
import * as Types from "./types"; import * as Types from "./types";
import { configContext } from "../../../../ConfigContext"; const apiVersion = "2024-12-01-preview";
const apiVersion = "2025-05-01-preview";
/* Lists the Tables under an existing Azure Cosmos DB database account. */ /* Lists the Tables under an existing Azure Cosmos DB database account. */
export async function listTables( export async function listTables(

View File

@@ -3,7 +3,7 @@
Run "npm run generateARMClients" to regenerate Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
*/ */
/* The List operation response, that contains the client encryption keys and their properties. */ /* The List operation response, that contains the client encryption keys and their properties. */
@@ -580,8 +580,6 @@ export interface DatabaseAccountGetProperties {
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */ /* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
enablePerRegionPerPartitionAutoscale?: boolean; enablePerRegionPerPartitionAutoscale?: boolean;
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
enableAllVersionsAndDeletesChangeFeed?: boolean;
} }
/* Properties to create and update Azure Cosmos DB database accounts. */ /* Properties to create and update Azure Cosmos DB database accounts. */
@@ -684,8 +682,6 @@ export interface DatabaseAccountCreateUpdateProperties {
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */ /* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
enablePerRegionPerPartitionAutoscale?: boolean; enablePerRegionPerPartitionAutoscale?: boolean;
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
enableAllVersionsAndDeletesChangeFeed?: boolean;
} }
/* Parameters to create and update Cosmos DB database accounts. */ /* Parameters to create and update Cosmos DB database accounts. */
@@ -791,8 +787,6 @@ export interface DatabaseAccountUpdateProperties {
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */ /* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
enablePerRegionPerPartitionAutoscale?: boolean; enablePerRegionPerPartitionAutoscale?: boolean;
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
enableAllVersionsAndDeletesChangeFeed?: boolean;
} }
/* Parameters for patching Azure Cosmos DB database account properties. */ /* Parameters for patching Azure Cosmos DB database account properties. */
@@ -1222,8 +1216,6 @@ export interface PhysicalPartitionThroughputInfoResource {
id: string; id: string;
/* Throughput of a physical partition */ /* Throughput of a physical partition */
throughput?: number; throughput?: number;
/* Target throughput of a physical partition */
targetThroughput?: number;
} }
/* Cosmos DB client encryption key resource object. */ /* Cosmos DB client encryption key resource object. */
@@ -1293,16 +1285,12 @@ export interface SqlContainerResource {
/* The configuration for defining Materialized Views. This must be specified only for creating a Materialized View container. */ /* The configuration for defining Materialized Views. This must be specified only for creating a Materialized View container. */
materializedViewDefinition?: MaterializedViewDefinition; materializedViewDefinition?: MaterializedViewDefinition;
/* Materialized Views defined on the container. */
materializedViews?: MaterializedViewDetails[];
/* List of computed properties */ /* List of computed properties */
computedProperties?: ComputedProperty[]; computedProperties?: ComputedProperty[];
/* The vector embedding policy for the container. */ /* The vector embedding policy for the container. */
vectorEmbeddingPolicy?: VectorEmbeddingPolicy; vectorEmbeddingPolicy?: VectorEmbeddingPolicy;
/* The FullText policy for the container. */
fullTextPolicy?: FullTextPolicy; fullTextPolicy?: FullTextPolicy;
} }
@@ -1335,14 +1323,6 @@ export interface VectorEmbeddingPolicy {
vectorEmbeddings?: VectorEmbedding[]; vectorEmbeddings?: VectorEmbedding[];
} }
/* Cosmos DB FullText Policy */
export interface FullTextPolicy {
/* The default language for a full text paths. */
defaultLanguage?: string;
/* List of FullText Paths */
fullTextPaths?: FullTextPath[];
}
/* undocumented */ /* undocumented */
export interface ExcludedPath { export interface ExcludedPath {
/* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */ /* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */
@@ -1381,7 +1361,7 @@ export interface VectorEmbedding {
/* The path to the vector field in the document. */ /* The path to the vector field in the document. */
path: string; path: string;
/* Indicates the data type of vector. */ /* Indicates the data type of vector. */
dataType: "float32" | "uint8" | "int8"; dataType: "float16" | "float32" | "uint8" | "int8";
/* The distance function to use for distance calculation in between vectors. */ /* The distance function to use for distance calculation in between vectors. */
distanceFunction: "euclidean" | "cosine" | "dotproduct"; distanceFunction: "euclidean" | "cosine" | "dotproduct";
@@ -1390,12 +1370,26 @@ export interface VectorEmbedding {
dimensions: number; dimensions: number;
} }
/* Represents the full text path specification. */ export interface FullTextPolicy {
/**
* The default language for the full text .
*/
defaultLanguage: string;
/**
* The paths to be indexed for full text search.
*/
fullTextPaths: FullTextPath[];
}
export interface FullTextPath { export interface FullTextPath {
/* The path to the full text field in the document. */ /**
* The path to be indexed for full text search.
*/
path: string; path: string;
/* The language of the full text field in the document. */ /**
language?: string; * The language for the full text path.
*/
language: string;
} }
/* List of composite path */ /* List of composite path */
@@ -1499,14 +1493,6 @@ export interface MaterializedViewDefinition {
definition: string; definition: string;
} }
/* MaterializedViewDetails, contains Id & _rid fields of materialized view. */
export interface MaterializedViewDetails {
/* Id field of Materialized container. */
id?: string;
/* _rid field of Materialized container. */
_rid?: string;
}
/* Cosmos DB SQL storedProcedure resource object */ /* Cosmos DB SQL storedProcedure resource object */
export interface SqlStoredProcedureResource { export interface SqlStoredProcedureResource {
/* Name of the Cosmos DB SQL storedProcedure */ /* Name of the Cosmos DB SQL storedProcedure */

View File

@@ -64,8 +64,7 @@ import {
getMsalInstance, getMsalInstance,
} from "../Utils/AuthorizationUtils"; } from "../Utils/AuthorizationUtils";
import { isInvalidParentFrameOrigin, shouldProcessMessage } from "../Utils/MessageValidation"; import { isInvalidParentFrameOrigin, shouldProcessMessage } from "../Utils/MessageValidation";
import { get, getReadOnlyKeys, listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts"; import { getReadOnlyKeys, listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
import * as Types from "../Utils/arm/generatedClients/cosmos/types";
import { applyExplorerBindings } from "../applyExplorerBindings"; import { applyExplorerBindings } from "../applyExplorerBindings";
// This hook will create a new instance of Explorer.ts and bind it to the DOM // This hook will create a new instance of Explorer.ts and bind it to the DOM
@@ -347,14 +346,6 @@ async function configureHostedWithAAD(config: AAD): Promise<Explorer> {
} }
} }
try { try {
// TO DO - Remove once we have ARG API support for enableMaterializedViews property
const databaseAccount: Types.DatabaseAccountGetResults = await get(
subscriptionId,
account.resourceGroup,
account.name,
);
config.databaseAccount.properties.enableMaterializedViews = databaseAccount.properties?.enableMaterializedViews;
updateUserContext({ updateUserContext({
databaseAccount: config.databaseAccount, databaseAccount: config.databaseAccount,
}); });

View File

@@ -103,7 +103,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
.forEach((tab) => tab.onCloseTabButtonClick()), .forEach((tab) => tab.onCloseTabButtonClick()),
closeTab: (tab: TabsBase): void => { closeTab: (tab: TabsBase): void => {
let tabIndex: number; let tabIndex: number;
const { activeTab, openedTabs, openedReactTabs } = get(); const { activeTab, openedTabs } = get();
const updatedTabs = openedTabs.filter((openedTab, index) => { const updatedTabs = openedTabs.filter((openedTab, index) => {
if (tab.tabId === openedTab.tabId) { if (tab.tabId === openedTab.tabId) {
tabIndex = index; tabIndex = index;
@@ -127,10 +127,6 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
set({ openedTabs: updatedTabs }); set({ openedTabs: updatedTabs });
if (updatedTabs.length === 0 && openedReactTabs.length > 0) {
set({ activeTab: undefined, activeReactTab: openedReactTabs[openedReactTabs.length - 1] });
}
get().persistTabsState(); get().persistTabsState();
}, },
closeAllNotebookTabs: (hardClose): void => { closeAllNotebookTabs: (hardClose): void => {

View File

@@ -1,20 +0,0 @@
import { useEffect, useState } from "react";
const useZoomLevel = (threshold: number = 2): boolean => {
const [isZoomed, setIsZoomed] = useState<boolean>(false);
useEffect(() => {
const checkZoom = () => {
const zoomLevel = window.devicePixelRatio;
setIsZoomed(zoomLevel >= threshold);
};
checkZoom();
window.addEventListener("resize", checkZoom);
return () => window.removeEventListener("resize", checkZoom);
}, [threshold]);
return isZoomed;
};
export default useZoomLevel;

View File

@@ -16,7 +16,7 @@ Results of this file should be checked into the repo.
*/ */
// CHANGE THESE VALUES TO GENERATE NEW CLIENTS // CHANGE THESE VALUES TO GENERATE NEW CLIENTS
const version = "2025-05-01-preview"; const version = "2024-12-01-preview";
/* The following are legal options for resourceName but you generally will only use cosmos: /* The following are legal options for resourceName but you generally will only use cosmos:
"cosmos" | "managedCassandra" | "mongorbac" | "notebook" | "privateEndpointConnection" | "privateLinkResources" | "cosmos" | "managedCassandra" | "mongorbac" | "notebook" | "privateEndpointConnection" | "privateLinkResources" |
"rbac" | "restorable" | "services" | "dataTransferService" "rbac" | "restorable" | "services" | "dataTransferService"

View File

@@ -30,7 +30,7 @@
<clear /> <clear />
<add name="X-Xss-Protection" value="1; mode=block" /> <add name="X-Xss-Protection" value="1; mode=block" />
<add name="X-Content-Type-Options" value="nosniff" /> <add name="X-Content-Type-Options" value="nosniff" />
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net dataexplorer-preview.azurewebsites.net" /> <add name="Content-Security-Policy" value="frame-src 'vscode:' frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net dataexplorer-preview.azurewebsites.net" />
</customHeaders> </customHeaders>
<redirectHeaders> <redirectHeaders>
<clear /> <clear />

View File

@@ -78,18 +78,11 @@ 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, javascriptRule]; const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule];
const envVars = { const envVars = {
GIT_SHA: gitSha, GIT_SHA: gitSha,
PORT: process.env.PORT || "1234", PORT: process.env.PORT || "1234",