Compare commits

..

2 Commits

Author SHA1 Message Date
Laurent Nguyen
d8ffcc4591 Merge branch 'master' into users/languy/fabric-appdevbtn-openssettings 2025-05-02 12:13:21 +02:00
Laurent Nguyen
e220e0e74b Fabric Home App Dev button opens Fabric UX extension Settings Connection tab 2025-04-29 14:31:56 +02:00
113 changed files with 1332 additions and 3795 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
<svg width="15" height="15" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="479" y="279" width="15" height="15"/></clipPath><clipPath id="clip1"><rect x="-0.287396" y="-0.171573" width="152381" height="152381"/></clipPath><image width="35" height="35" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAMAAAApB0NrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH4UExURQAAAASExCGs7CC//wB9vQB5uxSU1yaz8ySy9AB8uwB8vAB5uxOR1Say8yax8iaw8gB6vAB6uwB7uwB4uROQ1Cax8ySy8QCAvwB3ugB5ugB2uROP1CWw8yav8wCT2QCQ1QCP1wB2uQB1uBOO0yWv8yat8wCP1QCP1QCP1QCO1QCQ1wB1tQB2uAB3uAB1tx+k6SSt8ySt8QCN0wCO1ACM0wBzuQBztAB1twB0tgB0tiSr8SSs8gCM1ACM1ACEywBwtQBxtAB0tgBztQB0sySp8SSr8gCL0wCK0gCL0wCHzwByuABusgBztQBttiOq8gB6wQCI0QCJ0gB7wySn8SOo8gBxtABtsABwtgCFzwCI0gCG0QCJ0SKn8SKn8iKl8QBvsABvsgBvsQBtsABssgCCzACG0QCF0ACDzyKm8QBtrwBusABsrgCAzQCE0ACF0ACF0ACE0CKj8SKl8QBssQBtrwBtrwBsrgBsrwCK1QCBzwCD0ACA0B2d7CGj8QBsrABrrQBwrwCA3wCC0ACCzwCBzwB+zhGM3iGi8SKh8QCAzQCAzgCAzwB9zRCK3iCh8SGh8SCf8QB+zAB/zgB8zRCJ3SCg8SCf7wB+zQB6zBCI3CCe8CCf8CCe8CCf8SCf/wB7zAB4yxGJ3h6c8CCd7yCf7wmA0RqX60C//5CaUeMAAACodFJOUwA8XAho+//ncID/////53iM//////9wCKv/////gCiYILf///+AMPP/70wYw/+3lP+AXP+MKNv/+3uA/3z/+////+NAgP9c9/////+7HP+A///MgP9Y+////7scgP+AeP/////7/+NA/2D/iyTb//t4gP808//vUBjD/7eU/yifIAi3//////+Ar////////4CI/////3B//////+9/CGj7/+dwEDhYBCm1XqwAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAFpSURBVDhPY2AYBaiAkYkZXQgdsLCysXPgV8XJxc3Dy8vHj0eVgKCQsIioqKioGLoMDIhLSEpKSknLyMjIyKLLyckrgJUoCgsLCyspq6ioqKiiKVFT19DUYmDQ1tEFAT19AwMDA0M0NUbGxsbGJqZm5ubm5haWDFbW1tbWVmhqGGxsbW1t7ewdHB2dnBkYXFxdXV1d0NUwuLl7eHh4enn7+DIwMLj4+fn5Yaph8A8IDAwMDBIHsYNDQkJCgtFVMISGhUdERkZGRkUzMDDExMbGxsahK4lPSExKTklNTU1NS2dgiMvIyMhAV5OZlZWVlZ2Tm5eXl5dfwFBYVFRUVIimpriktKycgaGisgoEqmtqa2tr0dUw1NU3gKjGpuaWlpbWtvb29vYOdDUw0NjZ1dXd09vX39c3AV0OASZOmjR5ytSpU6dOQ5dBAtN7ZsycNXvO3HnoEshg/oKFixYvQRdFA0uXLUcXGqEAAH4FV0z+qQbjAAAAAElFTkSuQmCC" preserveAspectRatio="none" id="img2"></image><clipPath id="clip3"><path d="M44291.4 46947.4 187148 46947.4 187148 188823 44291.4 188823Z" fill-rule="evenodd" clip-rule="evenodd"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-479 -279)"><g clip-path="url(#clip1)" transform="matrix(0.000105 0 0 0.000105 479 279)"><g clip-path="url(#clip3)" transform="matrix(1 0 0 1.00692 -44291.4 -47272.4)"><use width="100%" height="100%" xlink:href="#img2" transform="scale(6709.45 6709.45)"></use></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

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

View File

@@ -211,12 +211,3 @@ a:focus {
.fileImportImg img {
filter: brightness(0) saturate(100%);
}
.tabPanesContainer {
overflow: auto !important;
}
.tabs-container {
min-height: 500px;
min-width: 500px;
}

211
package-lock.json generated
View File

@@ -10,7 +10,7 @@
"hasInstallScript": true,
"dependencies": {
"@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/identity": "4.5.0",
"@azure/msal-browser": "2.14.2",
@@ -290,71 +290,59 @@
"version": "2.6.2",
"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": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.20.0.tgz",
"integrity": "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g==",
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz",
"integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==",
"dependencies": {
"@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.8.0",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.11.0",
"@azure/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"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@azure/core-rest-pipeline/node_modules/agent-base": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
"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": {
"version": "2.6.2",
"license": "0BSD"
@@ -391,16 +379,15 @@
"license": "0BSD"
},
"node_modules/@azure/cosmos": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz",
"integrity": "sha512-0Ls3l1uWBBSphx6YRhnM+w7rSvq8qVugBCdO6kSiNuRYXEf6+YWLjbzz4e7L2kkz/6ScFdZIOJYP+XtkiRYOhA==",
"version": "4.2.0-beta.1",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz",
"integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==",
"dependencies": {
"@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.7.1",
"@azure/core-rest-pipeline": "^1.15.1",
"@azure/core-tracing": "^1.1.1",
"@azure/core-util": "^1.8.1",
"@azure/keyvault-keys": "^4.8.0",
"fast-json-stable-stringify": "^2.1.0",
"jsbi": "^4.3.0",
"priorityqueuejs": "^2.0.0",
@@ -505,66 +492,14 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"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": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.2.0.tgz",
"integrity": "sha512-0hKEzLhpw+ZTAfNJyRrn6s+V0nDWzXk9OjBr2TiGIu0OfMr5s2V4FpKLTAK3Ca5r5OKLbf4hkOGDPyiRjie/jA==",
"version": "1.0.4",
"license": "MIT",
"dependencies": {
"@typespec/ts-http-runtime": "^0.2.2",
"tslib": "^2.6.2"
"tslib": "^2.2.0"
},
"engines": {
"node": ">=18.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@azure/logger/node_modules/tslib": {
@@ -13139,56 +13074,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typespec/ts-http-runtime": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.2.2.tgz",
"integrity": "sha512-Gz/Sm64+Sq/vklJu1tt9t+4R2lvnud8NbTD/ZfpZtMiUX7YeVpCA8j6NSW8ptwcoLL+NmYANwqP8DV0q/bwl2w==",
"dependencies": {
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@typespec/ts-http-runtime/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"engines": {
"node": ">= 14"
}
},
"node_modules/@typespec/ts-http-runtime/node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@typespec/ts-http-runtime/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"node_modules/@ungap/url-search-params": {
"version": "0.2.2",
"license": "ISC"

View File

@@ -5,7 +5,7 @@
"main": "index.js",
"dependencies": {
"@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/identity": "4.5.0",
"@azure/msal-browser": "2.14.2",

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { monaco } from "Explorer/LazyMonaco";
import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
export enum QueryErrorSeverity {
Error = "Error",
@@ -102,9 +103,20 @@ export interface ErrorEnrichment {
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 {
message: string;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -126,5 +126,12 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
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

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

View File

@@ -21,7 +21,7 @@ export type DataExploreMessageV3 =
}
| {
type: FabricMessageTypes.OpenSettings;
settingsId: string;
params: [{ settingsId?: "About" | "Connection" }];
};
export interface GetCosmosTokenMessageOptions {
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";

View File

@@ -7,7 +7,6 @@ export interface ArmEntity {
type: string;
kind: string;
tags?: Tags;
resourceGroup?: string;
}
export interface DatabaseAccount extends ArmEntity {
@@ -211,7 +210,7 @@ export interface IndexingPolicy {
export interface VectorIndex {
path: string;
type: "flat" | "diskANN" | "quantizedFlat";
vectorIndexShardKey?: string[];
diskANNShardKey?: string;
indexingSearchListSize?: number;
quantizationByteSize?: number;
}
@@ -389,7 +388,7 @@ export interface VectorEmbeddingPolicy {
}
export interface VectorEmbedding {
dataType: "float32" | "uint8" | "int8";
dataType: "float16" | "float32" | "uint8" | "int8";
dimensions: number;
distanceFunction: "euclidean" | "cosine" | "dotproduct";
path: string;

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ import {
} from "Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputBucketsComponent";
import { useDatabases } from "Explorer/useDatabases";
import { isFabricNative } from "Platform/Fabric/FabricUtil";
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isFullTextSearchEnabled, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg";
@@ -188,10 +188,13 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.isGlobalSecondaryIndex =
!!this.collection?.materializedViewDefinition() || !!this.collection?.materializedViews();
this.isVectorSearchEnabled = isVectorSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
this.isFullTextSearchEnabled = userContext.apiType === "SQL";
this.isFullTextSearchEnabled = isFullTextSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
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"
this.isFixedContainer =
@@ -1071,11 +1074,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
databaseId: this.collection.databaseId,
collectionId: this.collection.id(),
currentOffer: this.collection.offer(),
autopilotThroughput: this.collection.offer?.()?.autoscaleMaxThroughput
? this.collection.offer?.()?.autoscaleMaxThroughput
autopilotThroughput: this.collection.offer().autoscaleMaxThroughput
? this.collection.offer().autoscaleMaxThroughput
: undefined,
manualThroughput: this.collection.offer?.()?.manualThroughput
? this.collection.offer?.()?.manualThroughput
manualThroughput: this.collection.offer().manualThroughput
? this.collection.offer().manualThroughput
: undefined,
throughputBuckets: this.state.throughputBuckets,
});
@@ -1091,7 +1094,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
currentOffer: this.collection.offer(),
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
throughputBuckets: this.throughputBucketsEnabled ? this.state.throughputBuckets : undefined,
};
if (this.hasProvisioningTypeChanged()) {
if (this.state.isAutoPilotSelected) {
@@ -1213,7 +1215,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
isFullTextSearchEnabled: this.isFullTextSearchEnabled,
shouldDiscardContainerPolicies: this.state.shouldDiscardContainerPolicies,
resetShouldDiscardContainerPolicyChange: this.resetShouldDiscardContainerPolicies,
isGlobalSecondaryIndex: this.isGlobalSecondaryIndex,
};
const indexingPolicyComponentProps: IndexingPolicyComponentProps = {
@@ -1342,7 +1343,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
});
}
if (this.throughputBucketsEnabled && !hasDatabaseSharedThroughput(this.collection) && this.offer) {
if (this.throughputBucketsEnabled) {
tabs.push({
tab: SettingsV2TabTypes.ThroughputBucketsTab,
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,

View File

@@ -22,7 +22,6 @@ export interface ContainerPolicyComponentProps {
isFullTextSearchEnabled: boolean;
shouldDiscardContainerPolicies: boolean;
resetShouldDiscardContainerPolicyChange: () => void;
isGlobalSecondaryIndex?: boolean;
}
export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> = ({

View File

@@ -143,39 +143,4 @@ describe("SubSettingsComponent", () => {
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
});
it("uniqueKey is visible", () => {
updateUserContext({
databaseAccount: {
properties: {
capabilities: [{ name: "EnableSQL" }],
},
} as DatabaseAccount,
});
const subSettingsComponent = new SubSettingsComponent(baseProps);
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(true);
});
it("uniqueKey not visible due to no keys", () => {
const props = {
...baseProps,
...(baseProps.collection.rawDataModel.uniqueKeyPolicy.uniqueKeys = []),
};
const subSettingsComponent = new SubSettingsComponent(props);
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(false);
});
it("uniqueKey not visible for API", () => {
const newContainer = new Explorer();
updateUserContext({
databaseAccount: {
properties: {
capabilities: [{ name: "EnableMongo" }],
},
} as DatabaseAccount,
});
const props = { ...baseProps, container: newContainer };
const subSettingsComponent = new SubSettingsComponent(props);
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(false);
});
});

View File

@@ -63,16 +63,12 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
private geospatialVisible: boolean;
private partitionKeyValue: string;
private partitionKeyName: string;
private uniqueKeyName: string;
private uniqueKeyValue: string;
constructor(props: SubSettingsComponentProps) {
super(props);
this.geospatialVisible = userContext.apiType === "SQL";
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
this.partitionKeyValue = this.getPartitionKeyValue();
this.uniqueKeyName = "Unique keys";
this.uniqueKeyValue = this.getUniqueKeyValue();
}
componentDidMount(): void {
@@ -355,28 +351,6 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
public getUniqueKeyVisible = (): boolean => {
return this.props.collection.rawDataModel.uniqueKeyPolicy?.uniqueKeys.length > 0 && userContext.apiType === "SQL";
};
private getUniqueKeyValue = (): string => {
const paths = this.props.collection.rawDataModel.uniqueKeyPolicy?.uniqueKeys?.[0]?.paths;
return paths?.join(", ") || "";
};
private getUniqueKeyComponent = (): JSX.Element => (
<Stack {...titleAndInputStackProps}>
{this.getUniqueKeyVisible() && (
<TextField
label={this.uniqueKeyName}
disabled
styles={getTextFieldStyles(undefined, undefined)}
defaultValue={this.uniqueKeyValue}
/>
)}
</Stack>
);
public render(): JSX.Element {
return (
<Stack {...subComponentStackProps}>
@@ -389,8 +363,6 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
{this.getPartitionKeyComponent()}
{this.getUniqueKeyComponent()}
</Stack>
);
}

View File

@@ -26,7 +26,7 @@ describe("ThroughputBucketsComponent", () => {
it("renders the correct number of buckets", () => {
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", () => {
@@ -36,14 +36,8 @@ describe("ThroughputBucketsComponent", () => {
];
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />);
const bucketLabels = screen.getAllByText(/Bucket \d+/).map((el) => el.textContent);
expect(bucketLabels).toEqual([
"Bucket 1 (Data Explorer Query Bucket)",
"Bucket 2",
"Bucket 3",
"Bucket 4",
"Bucket 5",
]);
const bucketLabels = screen.getAllByText(/Group \d+/).map((el) => el.textContent);
expect(bucketLabels).toEqual(["Group 1 (Data Explorer Query Bucket)", "Group 2", "Group 3", "Group 4", "Group 5"]);
});
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} />);
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(7);
expect(screen.getAllByText(/Group \d+/)).toHaveLength(7);
expect(screen.getByDisplayValue("50")).toBeInTheDocument();
expect(screen.getByDisplayValue("60")).toBeInTheDocument();
@@ -177,7 +171,7 @@ describe("ThroughputBucketsComponent", () => {
it("ensures default buckets are used when no buckets are provided", () => {
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />);
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5);
expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
expect(screen.getAllByDisplayValue("100")).toHaveLength(5);
});
});

View File

@@ -76,7 +76,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
value={bucket.maxThroughputPercentage}
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
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 } }}
disabled={bucket.maxThroughputPercentage === 100}
/>

View File

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

View File

@@ -231,34 +231,6 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
Non-hierarchically partitioned container.
</Text>
</Stack>
<Stack
tokens={
{
"childrenGap": 5,
}
}
>
<StyledTextFieldBase
defaultValue="/id"
disabled={true}
label="Unique keys"
styles={
{
"fieldGroup": {
"borderColor": "",
"height": 25,
"selectors": {
":disabled": {
"backgroundColor": undefined,
"borderColor": undefined,
},
},
"width": 300,
},
}
}
/>
</Stack>
</Stack>
`;
@@ -548,34 +520,6 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
Non-hierarchically partitioned container.
</Text>
</Stack>
<Stack
tokens={
{
"childrenGap": 5,
}
}
>
<StyledTextFieldBase
defaultValue="/id"
disabled={true}
label="Unique keys"
styles={
{
"fieldGroup": {
"borderColor": "",
"height": 25,
"selectors": {
":disabled": {
"backgroundColor": undefined,
"borderColor": undefined,
},
},
"width": 300,
},
}
}
/>
</Stack>
</Stack>
`;
@@ -825,34 +769,6 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
Non-hierarchically partitioned container.
</Text>
</Stack>
<Stack
tokens={
{
"childrenGap": 5,
}
}
>
<StyledTextFieldBase
defaultValue="/id"
disabled={true}
label="Unique keys"
styles={
{
"fieldGroup": {
"borderColor": "",
"height": 25,
"selectors": {
":disabled": {
"backgroundColor": undefined,
"borderColor": undefined,
},
},
"width": 300,
},
}
}
/>
</Stack>
</Stack>
`;
@@ -1167,34 +1083,6 @@ exports[`SubSettingsComponent renders 1`] = `
Non-hierarchically partitioned container.
</Text>
</Stack>
<Stack
tokens={
{
"childrenGap": 5,
}
}
>
<StyledTextFieldBase
defaultValue="/id"
disabled={true}
label="Unique keys"
styles={
{
"fieldGroup": {
"borderColor": "",
"height": 25,
"selectors": {
":disabled": {
"backgroundColor": undefined,
"borderColor": undefined,
},
},
"width": 300,
},
}
}
/>
</Stack>
</Stack>
`;
@@ -1483,33 +1371,5 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
Non-hierarchically partitioned container.
</Text>
</Stack>
<Stack
tokens={
{
"childrenGap": 5,
}
}
>
<StyledTextFieldBase
defaultValue="/id"
disabled={true}
label="Unique keys"
styles={
{
"fieldGroup": {
"borderColor": "",
"height": 25,
"selectors": {
":disabled": {
"backgroundColor": undefined,
"borderColor": undefined,
},
},
"width": 300,
},
}
}
/>
</Stack>
</Stack>
`;

View File

@@ -17,15 +17,7 @@ export const collection = {
includedPaths: [],
excludedPaths: [],
}),
rawDataModel: {
uniqueKeyPolicy: {
uniqueKeys: [
{
paths: ["/id"],
},
],
},
},
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
usageSizeInKB: ko.observable(100),
offer: ko.observable<DataModels.Offer>({
autoscaleMaxThroughput: undefined,

View File

@@ -71,18 +71,8 @@ exports[`SettingsComponent renders 1`] = `
"partitionKeyProperties": [
"partitionKey",
],
"rawDataModel": {
"uniqueKeyPolicy": {
"uniqueKeys": [
{
"paths": [
"/id",
],
},
],
},
},
"readSettings": [Function],
"uniqueKeyPolicy": {},
"usageSizeInKB": [Function],
"vectorEmbeddingPolicy": [Function],
}
@@ -163,18 +153,8 @@ exports[`SettingsComponent renders 1`] = `
"partitionKeyProperties": [
"partitionKey",
],
"rawDataModel": {
"uniqueKeyPolicy": {
"uniqueKeys": [
{
"paths": [
"/id",
],
},
],
},
},
"readSettings": [Function],
"uniqueKeyPolicy": {},
"usageSizeInKB": [Function],
"vectorEmbeddingPolicy": [Function],
}
@@ -198,32 +178,6 @@ exports[`SettingsComponent renders 1`] = `
timeToLiveSecondsBaseline={5}
/>
</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
headerText="Indexing Policy"
itemKey="IndexingPolicyTab"
@@ -320,18 +274,8 @@ exports[`SettingsComponent renders 1`] = `
"partitionKeyProperties": [
"partitionKey",
],
"rawDataModel": {
"uniqueKeyPolicy": {
"uniqueKeys": [
{
"paths": [
"/id",
],
},
],
},
},
"readSettings": [Function],
"uniqueKeyPolicy": {},
"usageSizeInKB": [Function],
"vectorEmbeddingPolicy": [Function],
}
@@ -460,18 +404,8 @@ exports[`SettingsComponent renders 1`] = `
"partitionKeyProperties": [
"partitionKey",
],
"rawDataModel": {
"uniqueKeyPolicy": {
"uniqueKeys": [
{
"paths": [
"/id",
],
},
],
},
},
"readSettings": [Function],
"uniqueKeyPolicy": {},
"usageSizeInKB": [Function],
"vectorEmbeddingPolicy": [Function],
}

View File

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

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 { CostEstimateText } from "Explorer/Controls/ThroughputInput/CostEstimateText/CostEstimateText";
import { useDatabases } from "Explorer/useDatabases";
import React, { FunctionComponent, useEffect, useState } from "react";
import * as Constants from "../../../Common/Constants";
@@ -10,8 +9,8 @@ import { userContext } from "../../../UserContext";
import { getCollectionName } from "../../../Utils/APITypeUtils";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import * as PricingUtils from "../../../Utils/PricingUtils";
import { CostEstimateText } from "./CostEstimateText/CostEstimateText";
import "./ThroughputInput.less";
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
export interface ThroughputInputProps {
isDatabase: boolean;
@@ -44,8 +43,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
if (
isFreeTier ||
isQuickstart ||
[Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(workloadType) ||
isFabricNative()
[Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(workloadType)
) {
defaultThroughput = AutoPilotUtils.autoPilotThroughput1K;
} else if (workloadType === Constants.WorkloadType.Production) {
@@ -232,92 +230,53 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
</div>
</Stack>
)}
{isAutoscaleSelected && (
<Stack className="throughputInputSpacing">
<Text style={{ marginTop: -2, fontSize: 12 }}>
Your container throughput will automatically scale up to the maximum value you select, from a minimum of 10%
of that value.
</Text>
<Stack horizontal verticalAlign="end" tokens={{ childrenGap: 8 }}>
<Stack tokens={{ childrenGap: 4 }}>
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}>
<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,
}}
<Text variant="small" aria-label="capacity calculator of azure cosmos db">
Estimate your required RU/s with{" "}
<Link
className="underlinedLink outlineNone"
target="_blank"
href="https://cosmos.azure.com/capacitycalculator/"
aria-label="capacity calculator of azure cosmos db"
>
x 10 =
</Text>
capacity calculator
</Link>
.
</Text>
<Stack tokens={{ childrenGap: 4 }}>
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}>
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
Maximum RU/s
</Text>
<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 horizontal>
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }} aria-label="maxRUDescription">
{isDatabase ? "Database" : getCollectionName()} Max RU/s
</Text>
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
</Stack>
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
<Stack className="throughputInputSpacing">
<Text variant="small" aria-label="ruDescription">
Estimate your required RU/s with&nbsp;
<Link
className="underlinedLink"
target="_blank"
href="https://cosmos.azure.com/capacitycalculator/"
aria-label="Capacity calculator"
>
capacity calculator
</Link>
.
</Text>
</Stack>
<Separator className="panelSeparator" style={{ paddingTop: -8, paddingBottom: -8 }} />
<TextField
id="autoscaleRUValueField"
type="number"
styles={{
fieldGroup: { width: 300, height: 27 },
field: { fontSize: 12 },
}}
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}
/>
<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>
)}
@@ -341,6 +300,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
</Text>
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
</Stack>
<TooltipHost
directionalHint={DirectionalHint.topLeftEdge}
content={
@@ -365,10 +325,11 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
errorMessage={throughputError}
/>
</TooltipHost>
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
</Stack>
)}
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
{throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && (
<Stack horizontal verticalAlign="start">
<Checkbox

View File

@@ -9,7 +9,6 @@ import {
Stack,
TextField,
} from "@fluentui/react";
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
import { VectorEmbedding, VectorIndex } from "Contracts/DataModels";
import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent";
import {
@@ -30,7 +29,6 @@ export interface IVectorEmbeddingPoliciesComponentProps {
discardChanges?: boolean;
onChangesDiscarded?: () => void;
disabled?: boolean;
isGlobalSecondaryIndex?: boolean;
}
export interface VectorEmbeddingPolicyData {
@@ -41,7 +39,8 @@ export interface VectorEmbeddingPolicyData {
indexType: VectorIndex["type"] | "none";
pathError: string;
dimensionsError: string;
vectorIndexShardKey?: string[];
diskANNShardKey?: string;
diskANNShardKeyError?: string;
indexingSearchListSize?: number;
indexingSearchListSizeError?: string;
quantizationByteSize?: number;
@@ -88,7 +87,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
discardChanges,
onChangesDiscarded,
disabled,
isGlobalSecondaryIndex,
}): JSX.Element => {
const onVectorEmbeddingPathError = (path: string, index?: number): string => {
let error = "";
@@ -134,6 +132,12 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
return error;
};
//TODO: no restrictions yet due to this field being removed for now.
// Uncomment and replace with validation code when field is reinstated
// const onDiskANNShardKeyError = (shardKey: string): string => {
// return "";
// };
const initializeData = (vectorEmbeddings: VectorEmbedding[], vectorIndexes: VectorIndex[]) => {
const mergedData: VectorEmbeddingPolicyData[] = [];
vectorEmbeddings.forEach((embedding) => {
@@ -143,7 +147,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
indexType: matchingIndex?.type || "none",
indexingSearchListSize: matchingIndex?.indexingSearchListSize || undefined,
quantizationByteSize: matchingIndex?.quantizationByteSize || undefined,
vectorIndexShardKey: matchingIndex?.vectorIndexShardKey || undefined,
pathError: onVectorEmbeddingPathError(embedding.path),
dimensionsError: onVectorEmbeddingDimensionError(embedding.dimensions, matchingIndex?.type || "none"),
});
@@ -183,7 +186,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
type: policy.indexType,
indexingSearchListSize: policy.indexingSearchListSize,
quantizationByteSize: policy.quantizationByteSize,
vectorIndexShardKey: policy.vectorIndexShardKey,
}) as VectorIndex,
);
const validationPassed = vectorEmbeddingPolicyData.every(
@@ -245,16 +247,20 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
setVectorEmbeddingPolicyData(vectorEmbeddings);
};
const onShardKeyChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value.trim();
const vectorEmbeddings = [...vectorEmbeddingPolicyData];
if (!vectorEmbeddings[index]?.vectorIndexShardKey?.[0] && !value.startsWith("/")) {
vectorEmbeddings[index].vectorIndexShardKey = ["/" + value];
} else {
vectorEmbeddings[index].vectorIndexShardKey = [value];
}
setVectorEmbeddingPolicyData(vectorEmbeddings);
};
// TODO: uncomment after Ignite
// DiskANNShardKey was removed for Ignite due to backend problems. Leaving this here as it will be reinstated immediately after Ignite
// const onDiskANNShardKeyChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => {
// const value = event.target.value.trim();
// const vectorEmbeddings = [...vectorEmbeddingPolicyData];
// if (!vectorEmbeddings[index]?.diskANNShardKey && !value.startsWith("/")) {
// vectorEmbeddings[index].diskANNShardKey = "/" + value;
// } else {
// vectorEmbeddings[index].diskANNShardKey = value;
// }
// const error = onDiskANNShardKeyError(value);
// vectorEmbeddings[index].diskANNShardKeyError = error;
// setVectorEmbeddingPolicyData(vectorEmbeddings);
// }
const onVectorEmbeddingPolicyChange = (
index: number,
@@ -286,11 +292,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
setVectorEmbeddingPolicyData(vectorEmbeddings);
};
const getQuantizationByteSizeTooltipContent = (): string => {
const containerName: string = isGlobalSecondaryIndex ? "global secondary index" : "container";
return `This is dynamically set by the ${containerName} if left blank, or it can be set to a fixed number`;
};
return (
<Stack tokens={{ childrenGap: 4 }}>
{vectorEmbeddingPolicyData &&
@@ -401,7 +402,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
styles={labelStyles}
>
Quantization byte size
<InfoTooltip>{getQuantizationByteSizeTooltipContent()}</InfoTooltip>
</Label>
<TextField
disabled={
@@ -431,18 +431,26 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
}
/>
</Stack>
<Stack style={{ marginLeft: "10px" }}>
<Label disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"} styles={labelStyles}>
Vector index shard key
</Label>
{/*TODO: uncomment after Ignite */}
{/* DiskANNShardKey was removed for Ignite due to backend problems. Leaving this here as it will be reinstated immediately after Ignite
<Stack
style={{ marginLeft: "10px" }}
>
<Label
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
styles={labelStyles}
>DiskANN shard key</Label>
<TextField
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
id={`vector-policy-vectorIndexShardKey-${index + 1}`}
id={`vector-policy-diskANNShardKey-${index + 1}`}
styles={textFieldStyles}
value={String(vectorEmbeddingPolicy.vectorIndexShardKey?.[0] ?? "")}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => onShardKeyChange(index, event)}
value={String(vectorEmbeddingPolicy.diskANNShardKey || "")}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
onDiskANNShardKeyChange(index, event)
}
/>
</Stack>
*/}
</Stack>
)}
</Stack>

View File

@@ -12,7 +12,6 @@ import { isFabricMirrored, isFabricMirroredKey, scheduleRefreshFabricToken } fro
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout";
@@ -31,7 +30,6 @@ import { readDatabases } from "../Common/dataAccess/readDatabases";
import * as DataModels from "../Contracts/DataModels";
import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse } from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { UploadDetailsRecord } from "../Contracts/ViewModels";
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
import { PhoenixClient } from "../Phoenix/PhoenixClient";
import * as ExplorerSettings from "../Shared/ExplorerSettings";
@@ -284,42 +282,6 @@ export default class Explorer {
}
}
public openInVsCode(): void {
const activeTab = useTabs.getState().activeTab;
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
const database = encodeURIComponent(activeTab?.collection?.databaseId);
const container = encodeURIComponent(activeTab?.collection?.id());
const baseUrl = `vscode://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
const openVSCodeDialogProps: DialogProps = {
linkProps: {
linkText: "Download Visual Studio Code",
linkUrl: "https://code.visualstudio.com/download",
},
isModal: true,
title: `Open your Azure Cosmos DB account in Visual Studio Code`,
subText: `Please ensure Visual Studio Code is installed on your device.
If you don't have it installed, please download it from the link below.`,
primaryButtonText: "Open in VS Code",
secondaryButtonText: "Cancel",
onPrimaryButtonClick: () => {
try {
window.location.href = vscodeUrl;
TelemetryProcessor.traceStart(Action.OpenVSCode);
} catch (error) {
logConsoleError(`Failed to open VS Code: ${getErrorMessage(error)}`);
}
},
onSecondaryButtonClick: () => {
useDialog.getState().closeDialog();
TelemetryProcessor.traceCancel(Action.OpenVSCode);
},
};
useDialog.getState().openDialog(openVSCodeDialogProps);
}
public async openCESCVAFeedbackBlade(): Promise<void> {
sendMessage({ type: MessageTypes.OpenCESCVAFeedbackBlade });
Logger.logInfo(
@@ -1116,8 +1078,8 @@ export default class Explorer {
}
}
public openUploadItemsPane(onUpload?: (data: UploadDetailsRecord[]) => void): void {
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane onUpload={onUpload} />);
public openUploadItemsPane(): void {
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
}
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
useSidePanel
@@ -1125,7 +1087,7 @@ export default class Explorer {
.openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />);
}
public getDownloadModalContent(fileName: string): JSX.Element {
public getDownloadModalConent(fileName: string): JSX.Element {
if (useNotebook.getState().isPhoenixNotebooks) {
return (
<>
@@ -1171,11 +1133,6 @@ export default class Explorer {
await this.initNotebooks(userContext.databaseAccount);
}
if (userContext.authType === AuthType.AAD && userContext.apiType === "SQL") {
const throughputBucketsEnabled = await featureRegistered(userContext.subscriptionId, "ThroughputBucketing");
updateUserContext({ throughputBucketsEnabled });
}
this.refreshSampleData();
}

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
import SettingsIcon from "../../../../images/settings_15x15.svg";
import SynapseIcon from "../../../../images/synapse-link.svg";
import VSCodeIcon from "../../../../images/vscode.svg";
import { AuthType } from "../../../AuthType";
import * as Constants from "../../../Common/Constants";
import { Platform, configContext } from "../../../ConfigContext";
@@ -61,10 +60,6 @@ export function createStaticCommandBarButtons(
addDivider();
buttons.push(addSynapseLink);
}
if (userContext.apiType !== "Gremlin") {
const addVsCode = createOpenVsCodeDialogButton(container);
buttons.push(addVsCode);
}
}
if (isDataplaneRbacSupported(userContext.apiType)) {
@@ -273,18 +268,6 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
};
}
function createOpenVsCodeDialogButton(container: Explorer): CommandButtonComponentProps {
const label = "Visual Studio Code";
return {
iconSrc: VSCodeIcon,
iconAlt: label,
onCommandClick: () => container.openInVsCode(),
commandButtonLabel: label,
hasPopup: false,
ariaLabel: label,
};
}
function createLoginForEntraIDButton(container: Explorer): CommandButtonComponentProps {
if (configContext.platform !== Platform.Portal) {
return undefined;
@@ -517,6 +500,6 @@ export function createPostgreButtons(container: Explorer): CommandButtonComponen
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
const addVsCode = createOpenVsCodeDialogButton(container);
return [openVCoreMongoTerminalButton, addVsCode];
return [openVCoreMongoTerminalButton];
}

View File

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

View File

@@ -173,20 +173,8 @@
.message {
flex-grow: 1;
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 InfoIcon from "../../../../images/info_color.svg";
import LoadingIcon from "../../../../images/loading.svg";
import WarningIcon from "../../../../images/warning.svg";
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
import { userContext } from "../../../UserContext";
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
@@ -92,9 +91,6 @@ export class NotificationConsoleComponent extends React.Component<
const numInfoItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Info,
).length;
const numWarningItems = this.state.allConsoleData.filter(
(data: ConsoleData) => data.type === ConsoleDataType.Warning,
).length;
return (
<div className="notificationConsoleContainer">
@@ -122,10 +118,6 @@ export class NotificationConsoleComponent extends React.Component<
<img src={infoBubbleIcon} alt="Info items" />
<span className="numInfoItems">{numInfoItems}</span>
</span>
<span className="notificationConsoleHeaderIconWithData">
<img src={WarningIcon} alt="Warning items" />
<span className="numWarningItems">{numWarningItems}</span>
</span>
</span>
{userContext.features.pr && <PrPreview pr={userContext.features.pr} />}
<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.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
{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="message" role="alert" aria-live="assertive">
{item.message}

View File

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

View File

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

View File

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

View File

@@ -40,12 +40,12 @@ import { PanelInfoErrorComponent } from "Explorer/Panes/PanelInfoErrorComponent"
import { PanelLoadingScreen } from "Explorer/Panes/PanelLoadingScreen";
import { useDatabases } from "Explorer/useDatabases";
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 { Action } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";
import { isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isFullTextSearchEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
export interface AddGlobalSecondaryIndexPanelProps {
@@ -75,8 +75,6 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
const [showErrorDetails, setShowErrorDetails] = useState<boolean>();
const [isExecuting, setIsExecuting] = useState<boolean>();
const showFullTextSearch: MutableRefObject<boolean> = useRef<boolean>(userContext.apiType === "SQL");
useEffect(() => {
const sourceContainerOptions: IDropdownOption[] = [];
useDatabases.getState().databases.forEach((database: Database) => {
@@ -142,6 +140,10 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
return isVectorSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput());
};
const showFullTextSearchParameters = (): boolean => {
return isFullTextSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput());
};
const getAnalyticalStorageTtl = (): number => {
if (!isSynapseLinkEnabled()) {
return undefined;
@@ -173,6 +175,11 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
return false;
}
if (globalSecondaryIndexThroughput > CollectionCreation.MaxRUPerPartition) {
setErrorMessage("Unsharded collections support up to 10,000 RUs");
return false;
}
if (showVectorSearchParameters()) {
if (!vectorPolicyValidated) {
setErrorMessage("Please fix errors in container vector policy");
@@ -226,7 +233,7 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
};
}
if (showFullTextSearch) {
if (showFullTextSearchParameters()) {
indexingPolicy.fullTextIndexes = fullTextIndexes;
}
@@ -381,11 +388,10 @@ export const AddGlobalSecondaryIndexPanel = (props: AddGlobalSecondaryIndexPanel
setVectorIndexingPolicy,
vectorPolicyValidated,
setVectorPolicyValidated,
isGlobalSecondaryIndex: true,
}}
/>
)}
{showFullTextSearch && (
{showFullTextSearchParameters() && (
<FullTextSearchComponent
{...{ fullTextPolicy, setFullTextPolicy, setFullTextIndexes, setFullTextPolicyValidated }}
/>

View File

@@ -47,7 +47,7 @@ export const ThroughputComponent = (props: ThroughputComponentProps): JSX.Elemen
<ThroughputInput
showFreeTierExceedThroughputTooltip={isFreeTierAccount() && !useDatabases.getState().isFirstResourceCreated()}
isDatabase={false}
isSharded={true}
isSharded={false}
isFreeTier={isFreeTierAccount()}
isQuickstart={false}
isGlobalSecondaryIndex={true}

View File

@@ -14,7 +14,6 @@ export interface VectorSearchComponentProps {
vectorIndexingPolicy: VectorIndex[];
setVectorIndexingPolicy: React.Dispatch<React.SetStateAction<VectorIndex[]>>;
setVectorPolicyValidated: React.Dispatch<React.SetStateAction<boolean>>;
isGlobalSecondaryIndex?: boolean;
}
export const VectorSearchComponent = (props: VectorSearchComponentProps): JSX.Element => {
@@ -24,7 +23,6 @@ export const VectorSearchComponent = (props: VectorSearchComponentProps): JSX.El
vectorIndexingPolicy,
setVectorIndexingPolicy,
setVectorPolicyValidated,
isGlobalSecondaryIndex,
} = props;
return (
@@ -51,7 +49,6 @@ export const VectorSearchComponent = (props: VectorSearchComponentProps): JSX.El
setVectorIndexingPolicy(vectorIndexingPolicy);
setVectorPolicyValidated(vectorPolicyValidated);
}}
isGlobalSecondaryIndex={isGlobalSecondaryIndex}
/>
</Stack>
</Stack>

View File

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

View File

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

View File

@@ -180,11 +180,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
? LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds)
: Constants.Queries.DefaultMaxWaitTimeInSeconds,
);
const [queryControlEnabled, setQueryControlEnabled] = useState<boolean>(
LocalStorageUtility.hasItem(StorageKey.QueryControlEnabled)
? LocalStorageUtility.getEntryString(StorageKey.QueryControlEnabled) === "true"
: false,
);
const [maxDegreeOfParallelism, setMaxDegreeOfParallelism] = useState<number>(
LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism)
? LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism)
@@ -209,7 +204,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
!isEmulator;
const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin" && !isEmulator;
const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin" && !isEmulator;
const shouldShowEnhancedQueryControl = userContext.apiType === "SQL";
const shouldShowParallelismOption = userContext.apiType !== "Gremlin" && !isEmulator;
const showEnableEntraIdRbac =
isDataplaneRbacSupported(userContext.apiType) &&
@@ -387,7 +381,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
LocalStorageUtility.setEntryNumber(StorageKey.MaxWaitTimeInSeconds, MaxWaitTimeInSeconds);
LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString());
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString());
LocalStorageUtility.setEntryString(StorageKey.QueryControlEnabled, queryControlEnabled.toString());
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism);
LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString());
LocalStorageUtility.setEntryString(StorageKey.CopilotSampleDBEnabled, copilotSampleDBEnabled.toString());
@@ -417,7 +410,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
`Updated items per page setting to ${LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage)}`,
);
logConsoleInfo(`${crossPartitionQueryEnabled ? "Enabled" : "Disabled"} cross-partition query feed option`);
logConsoleInfo(`${queryControlEnabled ? "Enabled" : "Disabled"} query control option`);
logConsoleInfo(
`Updated the max degree of parallelism query feed option to ${LocalStorageUtility.getEntryNumber(
StorageKey.MaxDegreeOfParellism,
@@ -616,7 +608,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
<RightPaneForm {...genericPaneProps}>
<div className={`paneMainContent ${styles.container}`}>
{!isFabricNative() && (
<Accordion className={`customAccordion ${styles.firstItem}`} collapsible>
<Accordion className={`customAccordion ${styles.firstItem}`}>
{shouldShowQueryPageOptions && (
<AccordionItem value="1">
<AccordionHeader>
@@ -768,6 +760,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
)}
</AccordionPanel>
</AccordionItem>
<AccordionItem value="5">
<AccordionHeader>
<div className={styles.header}>RU Limit</div>
@@ -950,38 +943,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</AccordionPanel>
</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 && (
<AccordionItem value="10">
<AccordionHeader>

View File

@@ -12,7 +12,6 @@ exports[`Settings Pane should render Default properly 1`] = `
>
<Accordion
className="customAccordion ___1uf6361_0000000 fz7g6wx"
collapsible={true}
>
<AccordionItem
value="1"
@@ -495,51 +494,6 @@ exports[`Settings Pane should render Default properly 1`] = `
</div>
</AccordionPanel>
</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
value="10"
>
@@ -619,7 +573,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
>
<Accordion
className="customAccordion ___1uf6361_0000000 fz7g6wx"
collapsible={true}
>
<AccordionItem
value="7"

View File

@@ -356,7 +356,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
value=""
>
<div
className="ms-TextField is-required root-116"
className="ms-TextField is-required root-110"
>
<div
className="ms-TextField-wrapper"
@@ -647,7 +647,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
}
>
<label
className="ms-Label root-127"
className="ms-Label root-121"
htmlFor="TextField0"
id="TextFieldLabel2"
>
@@ -656,13 +656,13 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
</LabelBase>
</StyledLabelBase>
<div
className="ms-TextField-fieldGroup fieldGroup-117"
className="ms-TextField-fieldGroup fieldGroup-111"
>
<input
aria-invalid={false}
aria-labelledby="TextFieldLabel2"
autoFocus={true}
className="ms-TextField-field field-118"
className="ms-TextField-field field-112"
id="TextField0"
name="collectionIdConfirmation"
onBlur={[Function]}
@@ -2464,7 +2464,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
>
<button
aria-label="Create"
className="ms-Button ms-Button--primary root-128"
className="ms-Button ms-Button--primary root-122"
data-is-focusable={true}
data-test="Panel/OkButton"
id="sidePanelOkButton"
@@ -2477,14 +2477,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
type="submit"
>
<span
className="ms-Button-flexContainer flexContainer-129"
className="ms-Button-flexContainer flexContainer-123"
data-automationid="splitbuttonprimary"
>
<span
className="ms-Button-textContainer textContainer-130"
className="ms-Button-textContainer textContainer-124"
>
<span
className="ms-Button-label label-132"
className="ms-Button-label label-126"
id="id__5"
key="id__5"
>

View File

@@ -2,13 +2,9 @@ import {
DetailsList,
DetailsListLayoutMode,
DirectionalHint,
FontIcon,
IColumn,
SelectionMode,
TooltipHost,
getTheme,
mergeStyles,
mergeStyleSets,
} from "@fluentui/react";
import { Upload } from "Common/Upload/Upload";
import { UploadDetailsRecord } from "Contracts/ViewModels";
@@ -18,41 +14,7 @@ import { getErrorMessage } from "../../Tables/Utilities";
import { useSelectedNode } from "../../useSelectedNode";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
const theme = getTheme();
const iconClass = mergeStyles({
verticalAlign: "middle",
maxHeight: "16px",
maxWidth: "16px",
});
const classNames = mergeStyleSets({
fileIconHeaderIcon: {
padding: 0,
fontSize: "16px",
},
fileIconCell: {
textAlign: "center",
selectors: {
"&:before": {
content: ".",
display: "inline-block",
verticalAlign: "middle",
height: "100%",
width: "0px",
visibility: "hidden",
},
},
},
error: [{ color: theme.semanticColors.errorIcon }, iconClass],
accept: [{ color: theme.semanticColors.successIcon }, iconClass],
warning: [{ color: theme.semanticColors.warningIcon }, iconClass],
});
export type UploadItemsPaneProps = {
onUpload?: (data: UploadDetailsRecord[]) => void;
};
export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpload }) => {
export const UploadItemsPane: FunctionComponent = () => {
const [files, setFiles] = useState<FileList>();
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
const [formError, setFormError] = useState<string>("");
@@ -75,8 +37,6 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpl
(uploadDetails) => {
setUploadFileData(uploadDetails.data);
setFiles(undefined);
// Emit the upload details to the parent component
onUpload && onUpload(uploadDetails.data);
},
(error: Error) => {
const errorMessage = getErrorMessage(error);
@@ -100,94 +60,44 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpl
};
const columns: IColumn[] = [
{
key: "icons",
name: "",
fieldName: "",
className: classNames.fileIconCell,
iconClassName: classNames.fileIconHeaderIcon,
isIconOnly: true,
minWidth: 16,
maxWidth: 16,
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
if (item.numFailed) {
const errorList = (
<ul
aria-label={"error list"}
style={{
margin: "5px 0",
paddingLeft: "20px",
listStyleType: "disc", // Explicitly set to use bullets (dots)
}}
>
{item.errors.map((error, i) => (
<li key={i} style={{ display: "list-item" }}>
{error}
</li>
))}
</ul>
);
return (
<TooltipHost
content={errorList}
id={`tooltip-${index}-${column.key}`}
directionalHint={DirectionalHint.bottomAutoEdge}
>
<FontIcon iconName="Error" className={classNames.error} aria-label="error" />
</TooltipHost>
);
} else if (item.numThrottled) {
return <FontIcon iconName="Warning" className={classNames.warning} aria-label="warning" />;
} else {
return <FontIcon iconName="Accept" className={classNames.accept} aria-label="accept" />;
}
},
},
{
key: "fileName",
name: "FILE NAME",
fieldName: "fileName",
minWidth: 120,
minWidth: 140,
maxWidth: 140,
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
const fieldContent = item.fileName;
return (
<TooltipHost
content={fieldContent}
id={`tooltip-${index}-${column.key}`}
directionalHint={DirectionalHint.bottomAutoEdge}
>
{fieldContent}
</TooltipHost>
);
},
},
{
key: "status",
name: "STATUS",
fieldName: "numSucceeded",
minWidth: 120,
minWidth: 140,
maxWidth: 140,
isRowHeader: true,
isResizable: true,
data: "string",
isPadded: true,
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
const fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
return (
<TooltipHost
content={fieldContent}
id={`tooltip-${index}-${column.key}`}
directionalHint={DirectionalHint.bottomAutoEdge}
>
{fieldContent}
</TooltipHost>
);
},
},
];
const _renderItemColumn = (item: UploadDetailsRecord, index: number, column: IColumn) => {
let fieldContent: string;
const tooltipId = `tooltip-${index}-${column.key}`;
switch (column.key) {
case "status":
fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
break;
default:
fieldContent = item.fileName;
}
return (
<TooltipHost content={fieldContent} id={tooltipId} directionalHint={DirectionalHint.rightCenter}>
{fieldContent}
</TooltipHost>
);
};
return (
<RightPaneForm {...props}>
<div className="paneMainContent">
@@ -205,6 +115,7 @@ export const UploadItemsPane: FunctionComponent<UploadItemsPaneProps> = ({ onUpl
<DetailsList
items={uploadFileData}
columns={columns}
onRenderItemColumn={_renderItemColumn}
selectionMode={SelectionMode.none}
layoutMode={DetailsListLayoutMode.justified}
isHeaderVisible={true}

View File

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

View File

@@ -1,14 +1,15 @@
/**
* Accordion top class
*/
import { makeStyles, tokens } from "@fluentui/react-components";
import { Link, makeStyles, tokens } from "@fluentui/react-components";
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
import { isFabricNative, isFabricNativeReadOnly, openSettingsConnectionTab } from "Platform/Fabric/FabricUtil";
import * as React from "react";
import { userContext } from "UserContext";
import CosmosDbBlackIcon from "../../../images/CosmosDB_black.svg";
import LinkIcon from "../../../images/Link_blue.svg";
import Explorer from "../Explorer";
export interface SplashScreenProps {
@@ -154,7 +155,7 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
title: "App development",
description: "Start here to use an SDK to build your apps",
icon: <LinkMultipleRegular />,
onClick: () => window.open("https://aka.ms/cosmosdbfabricsdk", "_blank"),
onClick: () => openSettingsConnectionTab(),
},
];
@@ -185,12 +186,12 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
{title}
</div>
{getSplashScreenButtons()}
{/* <div className={styles.footer}>
<div className={styles.footer}>
Need help?{" "}
<Link href="https://aka.ms/cosmosdbfabricdocs" target="_blank">
Learn more <img src={LinkIcon} alt="Learn more" />
</Link>
</div> */}
</div>
</CosmosFluentProvider>
</>
);

View File

@@ -30,21 +30,6 @@
margin: 0px auto;
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 {
.flex-display();

View File

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

View File

@@ -43,52 +43,32 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter
await ensureCloudShellProviderRegistered();
resolvedRegion = determineCloudShellRegion();
resolvedRegion = determineCloudShellRegion();
terminal.writeln(formatWarningMessage("⚠️ IMPORTANT: Azure Cloud Shell Region Notice ⚠️"));
terminal.writeln(
formatInfoMessage(
"The Cloud Shell environment will operate in a region that may differ from your database's region.",
),
);
terminal.writeln(formatInfoMessage("This has two potential implications:"));
terminal.writeln(formatInfoMessage("1. Performance Impact:"));
terminal.writeln(
formatInfoMessage(" Commands may experience higher latency due to geographic distance between regions."),
);
terminal.writeln(formatInfoMessage("2. Data Compliance Considerations:"));
terminal.writeln(
formatInfoMessage(
" Data processed through this shell could temporarily reside in a different geographic region,",
),
);
terminal.writeln(
formatInfoMessage(" which may affect compliance with data residency requirements or regulations specific"),
);
terminal.writeln(formatInfoMessage(" to your organization."));
terminal.writeln("");
terminal.writeln("\x1b[94mFor more information on Azure Cosmos DB data governance and compliance, please visit:");
terminal.writeln("\x1b[94mhttps://learn.microsoft.com/en-us/azure/cosmos-db/data-residency\x1b[0m");
// Ask for user consent for region
const consentGranted = await askConfirmation(terminal, formatWarningMessage("Do you wish to proceed?"));
const consentGranted = await askConfirmation(
terminal,
formatWarningMessage(
"The shell environment may be operating in a region different from that of the database, which could impact performance or data compliance. Do you wish to proceed?",
),
);
// Track user decision
TelemetryProcessor.trace(
Action.CloudShellUserConsent,
consentGranted ? ActionModifiers.Success : ActionModifiers.Cancel,
{
dataExplorerArea: Areas.CloudShell,
shellType: TerminalKind[shellType],
isConsent: consentGranted,
region: resolvedRegion,
},
startKey,
{ dataExplorerArea: Areas.CloudShell },
);
if (!consentGranted) {
TelemetryProcessor.traceCancel(
Action.CloudShellTerminalSession,
{
shellType: TerminalKind[shellType],
dataExplorerArea: Areas.CloudShell,
region: resolvedRegion,
isConsent: false,
},
startKey,
);
terminal.writeln(
formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."),
);
@@ -282,27 +262,28 @@ export const configureSocketConnection = async (
};
export const sendTerminalStartupCommands = (socket: WebSocket, initCommands: string): void => {
// ensures connections don't remain open indefinitely by implementing an automatic timeout after 120 minutes.
const keepSocketAlive = (socket: WebSocket) => {
if (socket.readyState === WebSocket.OPEN) {
if (pingCount >= MAX_PING_COUNT) {
socket.close();
} else {
pingCount++;
// The code uses a recursive setTimeout pattern rather than setInterval,
// which ensures each new ping only happens after the previous one completes
// and naturally stops if the socket closes.
keepAliveID = setTimeout(() => keepSocketAlive(socket), 1000);
}
}
};
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(initCommands);
keepSocketAlive(socket);
} else {
socket.onopen = () => {
socket.send(initCommands);
// ensures connections don't remain open indefinitely by implementing an automatic timeout after 20 minutes.
const keepSocketAlive = (socket: WebSocket) => {
if (socket.readyState === WebSocket.OPEN) {
if (pingCount >= MAX_PING_COUNT) {
socket.close();
} else {
socket.send("");
pingCount++;
// The code uses a recursive setTimeout pattern rather than setInterval,
// which ensures each new ping only happens after the previous one completes
// and naturally stops if the socket closes.
keepAliveID = setTimeout(() => keepSocketAlive(socket), 1000);
}
}
};
keepSocketAlive(socket);
};
}

View File

@@ -22,12 +22,6 @@ export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close thi
* the required methods.
*/
export abstract class AbstractShellHandler {
/**
* The name of the application using this shell handler.
* This is used for telemetry and logging purposes.
*/
protected APP_NAME = "CosmosExplorerTerminal";
abstract getShellName(): string;
abstract getSetUpCommands(): string[];
abstract getConnectionCommand(): string;
@@ -62,30 +56,4 @@ export abstract class AbstractShellHandler {
return allCommands.join("\n").concat("\n");
}
/**
* Setup commands for MongoDB shell:
*
* 1. Check if mongosh is already installed
* 2. Download mongosh package if not installed
* 3. Extract the package to access mongosh binaries
* 4. Move extracted files to ~/mongosh directory
* 5. Add mongosh binary path to system PATH
* 6. Apply PATH changes by sourcing .bashrc
*
* Each command runs conditionally only if mongosh
* is not already present in the environment.
*/
protected mongoShellSetupCommands(): string[] {
const PACKAGE_VERSION: string = "2.5.0";
return [
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
`if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh/bin && mv mongosh-${PACKAGE_VERSION}-linux-x64/bin/mongosh ~/mongosh/bin/ && chmod +x ~/mongosh/bin/mongosh; fi`,
`if ! command -v mongosh &> /dev/null; then rm -rf mongosh-${PACKAGE_VERSION}-linux-x64 mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
"source ~/.bashrc",
];
}
}

View File

@@ -87,7 +87,7 @@ describe("CassandraShellHandler", () => {
});
test("should return correct connection command", () => {
const expectedCommand = `cqlsh test-endpoint.cassandra.cosmos.azure.com 10350 -u test-account -p test-key --ssl`;
const expectedCommand = "cqlsh test-endpoint.cassandra.cosmos.azure.com 10350 -u test-account -p test-key --ssl";
expect(handler.getConnectionCommand()).toBe(expectedCommand);
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-endpoint.cassandra.cosmos.azure.com:443/");

View File

@@ -68,7 +68,7 @@ describe("MongoShellHandler", () => {
const commands = mongoShellHandler.getSetUpCommands();
expect(Array.isArray(commands)).toBe(true);
expect(commands.length).toBe(7);
expect(commands.length).toBe(6);
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
});
});
@@ -91,7 +91,7 @@ describe("MongoShellHandler", () => {
const command = mongoShellHandler.getConnectionCommand();
expect(command).toBe(
"mongosh mongodb://test-mongo.documents.azure.com:10255?appName=CosmosExplorerTerminal --username test-account --password test-key --tls --tlsAllowInvalidCertificates",
"mongosh --host test-mongo.documents.azure.com --port 10255 --username test-account --password test-key --tls --tlsAllowInvalidCertificates",
);
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-mongo.documents.azure.com:443/");

View File

@@ -2,6 +2,8 @@ import { userContext } from "../../../../UserContext";
import { getHostFromUrl } from "../Utils/CommonUtils";
import { AbstractShellHandler } from "./AbstractShellHandler";
const PACKAGE_VERSION: string = "2.5.0";
export class MongoShellHandler extends AbstractShellHandler {
private _key: string;
private _endpoint: string | undefined;
@@ -16,7 +18,14 @@ export class MongoShellHandler extends AbstractShellHandler {
}
public getSetUpCommands(): string[] {
return this.mongoShellSetupCommands();
return [
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
`if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh && mv mongosh-${PACKAGE_VERSION}-linux-x64/* ~/mongosh/; fi`,
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
"source ~/.bashrc",
];
}
public getConnectionCommand(): string {
@@ -28,17 +37,9 @@ export class MongoShellHandler extends AbstractShellHandler {
if (!dbName) {
return "echo 'Database name not found.'";
}
return (
"mongosh mongodb://" +
getHostFromUrl(this._endpoint) +
":10255?appName=" +
this.APP_NAME +
" --username " +
dbName +
" --password " +
this._key +
" --tls --tlsAllowInvalidCertificates"
);
return `mongosh --host ${getHostFromUrl(this._endpoint)} --port 10255 --username ${dbName} --password ${
this._key
} --tls --tlsAllowInvalidCertificates`;
}
public getTerminalSuppressedData(): string {

View File

@@ -54,7 +54,7 @@ export class PostgresShellHandler extends AbstractShellHandler {
// All Azure Cosmos DB PostgreSQL deployments follow this convention.
// Ref. https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/reference-limits#database-creation
const loginName = userContext.postgresConnectionStrParams.adminLogin;
return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require --set=application_name=${this.APP_NAME}`;
return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require`;
}
public getTerminalSuppressedData(): string {

View File

@@ -44,7 +44,7 @@ describe("VCoreMongoShellHandler", () => {
const commands = vcoreMongoShellHandler.getSetUpCommands();
expect(Array.isArray(commands)).toBe(true);
expect(commands.length).toBe(7);
expect(commands.length).toBe(6);
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
expect(commands[0]).toContain("mongosh not found");
});

View File

@@ -1,6 +1,8 @@
import { userContext } from "../../../../UserContext";
import { AbstractShellHandler } from "./AbstractShellHandler";
const PACKAGE_VERSION: string = "2.5.0";
export class VCoreMongoShellHandler extends AbstractShellHandler {
private _endpoint: string | undefined;
@@ -13,8 +15,28 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
return "MongoDB VCore";
}
/**
* Setup commands for MongoDB VCore shell:
*
* 1. Check if mongosh is already installed
* 2. Download mongosh package if not installed
* 3. Extract the package to access mongosh binaries
* 4. Move extracted files to ~/mongosh directory
* 5. Add mongosh binary path to system PATH
* 6. Apply PATH changes by sourcing .bashrc
*
* Each command runs conditionally only if mongosh
* is not already present in the environment.
*/
public getSetUpCommands(): string[] {
return this.mongoShellSetupCommands();
return [
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
`if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh && mv mongosh-${PACKAGE_VERSION}-linux-x64/* ~/mongosh/; fi`,
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
"source ~/.bashrc",
];
}
public getConnectionCommand(): string {
@@ -23,7 +45,7 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
}
const userName = userContext.vcoreMongoConnectionParams.adminLogin;
return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000&appName=${this.APP_NAME}"`;
return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000"`;
}
public getTerminalSuppressedData(): string {

View File

@@ -1,6 +1,5 @@
import { AbstractShellHandler } from "Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler";
import { IDisposable, ITerminalAddon, Terminal } from "@xterm/xterm";
import { AbstractShellHandler } from "../ShellTypes/AbstractShellHandler";
import { formatErrorMessage } from "./TerminalLogFormats";
interface IAttachOptions {
bidirectional?: boolean;
@@ -57,27 +56,8 @@ export class AttachAddon implements ITerminalAddon {
this._disposables.push(terminal.onBinary((data) => this._sendBinary(data)));
}
this._disposables.push(addSocketListener(this._socket, "close", () => this._handleSocketClose(terminal)));
this._disposables.push(addSocketListener(this._socket, "error", () => this._handleSocketClose(terminal)));
}
/**
* Handles socket close events by terminating processes and showing a message
*/
private _handleSocketClose(terminal: Terminal): void {
if (terminal) {
terminal.writeln(
formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."),
);
// Send exit command to terminal
if (this._bidirectional) {
terminal.write(formatErrorMessage("exit\r\n"));
}
}
// Clean up resources
this.dispose();
this._disposables.push(addSocketListener(this._socket, "close", () => this.dispose()));
this._disposables.push(addSocketListener(this._socket, "error", () => this.dispose()));
}
/**

View File

@@ -26,6 +26,7 @@ import { useDialog } from "Explorer/Controls/Dialog";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { InputDataList, InputDatalistDropdownOptionSection } from "Explorer/Controls/InputDataList/InputDataList";
import { ProgressModalDialog } from "Explorer/Controls/ProgressModalDialog";
import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { querySampleDocuments, readSampleDocument } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import {
@@ -63,7 +64,7 @@ import * as Logger from "../../../Common/Logger";
import * as MongoProxyClient from "../../../Common/MongoProxyClient";
import * as DataModels from "../../../Contracts/DataModels";
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 QueryUtils from "../../../Utils/QueryUtils";
import { defaultQueryFields, extractPartitionKeyValues } from "../../../Utils/QueryUtils";
@@ -143,13 +144,6 @@ export const useDocumentsTabStyles = makeStyles({
deleteProgressContent: {
paddingTop: tokens.spacingVerticalL,
},
smallScreenContent: {
"@media (max-width: 420px)": {
flexWrap: "wrap",
minHeight: "max-content",
padding: "4px",
},
},
});
export class DocumentsTabV2 extends TabsBase {
@@ -308,6 +302,7 @@ type UiKeyboardEvent = (e: KeyboardEvent | React.SyntheticEvent<Element, Event>)
// Export to expose to unit tests
export type ButtonsDependencies = {
_collection: ViewModels.CollectionBase;
selectedRows: Set<TableRowId>;
editorState: ViewModels.DocumentExplorerState;
isPreferredApiMongoDB: boolean;
@@ -318,7 +313,26 @@ export type ButtonsDependencies = {
onSaveExistingDocumentClick: UiKeyboardEvent;
onRevertExistingDocumentClick: 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
@@ -331,6 +345,7 @@ export const UPLOAD_BUTTON_ID = "uploadItemBtn";
// Export to expose in unit tests
export const getTabsButtons = ({
_collection,
selectedRows,
editorState,
isPreferredApiMongoDB,
@@ -341,7 +356,6 @@ export const getTabsButtons = ({
onSaveExistingDocumentClick,
onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
}: ButtonsDependencies): CommandButtonComponentProps[] => {
if (isFabric() && userContext.fabricContext?.isReadOnly) {
// All the following buttons require write access
@@ -453,20 +467,7 @@ export const getTabsButtons = ({
}
if (!isPreferredApiMongoDB) {
const label = "Upload Item";
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(),
});
buttons.push(createUploadButton(_collection.container));
}
return buttons;
@@ -671,7 +672,6 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
collection: CollectionBase;
}>(undefined);
const [bulkDeleteMode, setBulkDeleteMode] = useState<"inProgress" | "completed" | "aborting" | "aborted">(undefined);
const [abortController, setAbortController] = useState<AbortController | undefined>(undefined);
const setKeyboardActions = useKeyboardActionGroup(KeyboardActionGroup.ACTIVE_TAB);
@@ -699,19 +699,13 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
return;
}
if (bulkDeleteProcess.pendingIds.length === 0 && bulkDeleteProcess.throttledIds.length === 0) {
// Successfully deleted all documents
if (
(bulkDeleteProcess.pendingIds.length === 0 && bulkDeleteProcess.throttledIds.length === 0) ||
bulkDeleteMode === "aborting"
) {
// Successfully deleted all documents or operation was aborted
bulkDeleteOperation.onCompleted(bulkDeleteProcess.successfulIds);
setBulkDeleteMode("completed");
return;
}
if (bulkDeleteMode === "aborting") {
// Operation was aborted
abortController?.abort();
bulkDeleteOperation.onCompleted(bulkDeleteProcess.successfulIds);
setBulkDeleteMode("aborted");
setAbortController(undefined);
setBulkDeleteMode(bulkDeleteMode === "aborting" ? "aborted" : "completed");
return;
}
@@ -719,10 +713,8 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const newPendingIds = bulkDeleteProcess.pendingIds.concat(bulkDeleteProcess.throttledIds);
const timeout = bulkDeleteProcess.beforeExecuteMs || 0;
const ac = new AbortController();
setAbortController(ac);
setTimeout(() => {
deleteNoSqlDocuments(bulkDeleteOperation.collection, [...newPendingIds], ac.signal)
deleteNoSqlDocuments(bulkDeleteOperation.collection, [...newPendingIds])
.then((deleteResult) => {
let retryAfterMilliseconds = 0;
const newSuccessful: DocumentId[] = [];
@@ -737,7 +729,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
} else if (result.statusCode >= 400) {
newFailed.push(result.documentId);
logConsoleError(
`Failed to delete document ${result.documentId.id()} with status code ${result.statusCode}`,
`Failed to delete document ${result.documentId.id} with status code ${result.statusCode}`,
);
}
});
@@ -878,6 +870,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
}
updateNavbarWithTabsButtons(isTabActive, {
_collection,
selectedRows,
editorState,
isPreferredApiMongoDB,
@@ -888,7 +881,6 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onSaveExistingDocumentClick,
onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
});
}, []);
@@ -1294,47 +1286,11 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
);
}, [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
useEffect(
() =>
updateNavbarWithTabsButtons(isTabActive, {
_collection,
selectedRows,
editorState,
isPreferredApiMongoDB,
@@ -1343,11 +1299,11 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onSaveNewDocumentClick,
onRevertNewDocumentClick,
onSaveExistingDocumentClick,
onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
onRevertExistingDocumentClick: onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick: onDeleteExistingDocumentsClick,
}),
[
_collection,
selectedRows,
editorState,
isPreferredApiMongoDB,
@@ -1358,7 +1314,6 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
onSaveExistingDocumentClick,
onRevertExistingDocumentClick,
onDeleteExistingDocumentsClick,
onUploadDocumentsClick,
isTabActive,
],
);
@@ -2147,7 +2102,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
return (
<CosmosFluentProvider className={styles.container}>
<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>}
<InputDataList
dropdownOptions={getFilterChoices()}

View File

@@ -15,7 +15,7 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
}
>
<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"
>
<span>

View File

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

View File

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

View File

@@ -32,7 +32,6 @@ enum ResultsTabs {
const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, executeQueryDocumentsPage }) => {
const styles = useQueryTabStyles();
/* eslint-disable react/prop-types */
const queryResultsString = queryResults
? isMongoDB
? MongoUtility.tojson(queryResults.documents, undefined, false)
@@ -48,172 +47,6 @@ const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, execu
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 (
<>
<div className={styles.queryResultsBar}>
@@ -234,7 +67,6 @@ const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, execu
aria-label="Copy"
onClick={onClickCopyResults}
/>
<ExportResults />
</div>
<div className={styles.queryResultsViewer}>
<EditorReact language={"json"} content={queryResultsString} isReadOnly={true} ariaLabel={"Query results"} />

View File

@@ -25,9 +25,6 @@ export const useQueryTabStyles = makeStyles({
height: "100%",
display: "flex",
flexDirection: "column",
"@media (max-width: 420px)": {
overflow: "scroll",
},
},
queryResultsMessage: {
...shorthands.margin("5px"),
@@ -41,9 +38,6 @@ export const useQueryTabStyles = makeStyles({
display: "flex",
rowGap: "12px",
flexDirection: "column",
"@media (max-width: 420px)": {
height: "auto",
},
},
queryResultsTabContentContainer: {
display: "flex",
@@ -99,12 +93,4 @@ export const useQueryTabStyles = makeStyles({
display: "flex",
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 errorIcon from "../../../images/close-black.svg";
import errorQuery from "../../../images/error_no_outline.svg";
import warningIconSvg from "../../../images/warning.svg";
import { useObservable } from "../../hooks/useObservable";
import { ReactTabKind, useTabs } from "../../hooks/useTabs";
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 }}>
{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) && (
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
)}
@@ -198,20 +194,6 @@ const ErrorIcon = ({ tab, active }: { tab: Tab; active: boolean }) => (
</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 }) {
const ref = useRef<HTMLDivElement>();
const attrs = {

View File

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

View File

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

View File

@@ -128,6 +128,15 @@ export const checkDatabaseResourceTokensValidity = (tokenTimestamp: number): voi
}
};
/**
* Open the connection tab in the settings page of the Fabric UX extension
*/
export const openSettingsConnectionTab = (): void => {
if (configContext.platform === Platform.Fabric) {
sendCachedDataMessage(FabricMessageTypes.OpenSettings, [{ settingsId: "Connection" }]);
}
};
export const isFabric = (): boolean => configContext.platform === Platform.Fabric;
export const isFabricMirroredKey = (): boolean =>
isFabric() && userContext.fabricContext?.artifactType === CosmosDbArtifactType.MIRRORED_KEY;

View File

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

View File

@@ -149,7 +149,6 @@ export enum Action {
UploadDocuments, // Used in Fabric. Please do not rename.
CloudShellUserConsent,
CloudShellTerminalSession,
OpenVSCode,
}
export const ActionModifiers = {

View File

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

View File

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

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",
undefined,
container.getDownloadModalContent(name),
container.getDownloadModalConent(name),
);
}
export async function downloadNotebookItem(

View File

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

View File

@@ -1,7 +1,4 @@
import { PartitionKey, PartitionKeyDefinition } from "@azure/cosmos";
import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
import { userContext } from "UserContext";
import { logConsoleWarning } from "Utils/NotificationConsoleUtils";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
@@ -89,18 +86,6 @@ export const queryPagesUntilContentPresent = async (
results.roundTrips = roundTrips;
results.requestCharge = Number(results.requestCharge) + netRequestCharge;
netRequestCharge = Number(results.requestCharge);
if (results.hasMoreResults && userContext.apiType === "SQL" && ruThresholdEnabled()) {
const ruThreshold: number = getRUThreshold();
if (netRequestCharge > ruThreshold) {
logConsoleWarning(
`Warning: Query has exceeded the Request Unit threshold of ${ruThreshold} RUs. Query results show only those documents returned before the threshold was exceeded`,
);
results.ruThresholdExceeded = true;
return results;
}
}
const resultsMetadata = {
hasMoreResults: results.hasMoreResults,
itemCount: results.itemCount,
@@ -139,7 +124,7 @@ export const extractPartitionKeyValues = (
documentContent: any,
partitionKeyDefinition: PartitionKeyDefinition,
): PartitionKey[] => {
if (!partitionKeyDefinition.paths || partitionKeyDefinition.paths.length === 0) {
if (!partitionKeyDefinition.paths || partitionKeyDefinition.paths.length === 0 || partitionKeyDefinition.systemKey) {
return undefined;
}
@@ -151,7 +136,7 @@ export const extractPartitionKeyValues = (
if (value !== undefined) {
partitionKeyValues.push(value);
} else if (!partitionKeyDefinition.systemKey) {
} else {
partitionKeyValues.push({});
}
});

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
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
export async function listCassandraKeyspaces(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and collection. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and database. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and region. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Retrieves the properties of an existing Azure Cosmos DB database account. */
export async function get(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Lists the graphs under an existing Azure Cosmos DB database account. */
export async function listGraphs(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
export async function listGremlinDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* List Cosmos DB locations and their properties */
export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> {

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
export async function listMongoDBDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
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 * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2024-12-01-preview";
/* Lists all of the available Cosmos DB Resource Provider operations. */
export async function list(): Promise<Types.OperationListResult> {

Some files were not shown because too many files have changed in this diff Show More