mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-26 12:21:23 +00:00
Compare commits
2 Commits
unit-tests
...
users/lang
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8ffcc4591 | ||
|
|
e220e0e74b |
File diff suppressed because one or more lines are too long
@@ -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 |
@@ -2869,7 +2869,6 @@ a:link {
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
overflow-x: clip;
|
||||
min-height: fit-content;
|
||||
}
|
||||
|
||||
.uniqueIndexesContainer {
|
||||
|
||||
@@ -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
211
package-lock.json
generated
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
6
preview/package-lock.json
generated
6
preview/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ export enum PaneKind {
|
||||
GlobalSettings,
|
||||
AdHocAccess,
|
||||
SwitchDirectory,
|
||||
QuickStart,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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} />,
|
||||
|
||||
@@ -22,7 +22,6 @@ export interface ContainerPolicyComponentProps {
|
||||
isFullTextSearchEnabled: boolean;
|
||||
shouldDiscardContainerPolicies: boolean;
|
||||
resetShouldDiscardContainerPolicyChange: () => void;
|
||||
isGlobalSecondaryIndex?: boolean;
|
||||
}
|
||||
|
||||
export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> = ({
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -285,7 +285,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
serverId,
|
||||
numberOfRegions,
|
||||
isMultimaster,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -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>
|
||||
`;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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],
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
<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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,10 +95,3 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.newVertexComponent {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -13,5 +13,4 @@ export enum ConsoleDataType {
|
||||
Info = 0,
|
||||
Error = 1,
|
||||
InProgress = 2,
|
||||
Warning = 3,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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">* </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">* </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">* </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">* </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">* </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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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]}
|
||||
|
||||
@@ -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 }}
|
||||
/>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/");
|
||||
|
||||
@@ -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/");
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"} />
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -21,7 +21,6 @@ export enum StorageKey {
|
||||
DatabaseAccountId,
|
||||
EncryptedKeyToken,
|
||||
IsCrossPartitionQueryEnabled,
|
||||
QueryControlEnabled,
|
||||
MaxDegreeOfParellism,
|
||||
IsGraphAutoVizDisabled,
|
||||
TenantId,
|
||||
|
||||
@@ -149,7 +149,6 @@ export enum Action {
|
||||
UploadDocuments, // Used in Fabric. Please do not rename.
|
||||
CloudShellUserConsent,
|
||||
CloudShellTerminalSession,
|
||||
OpenVSCode,
|
||||
}
|
||||
|
||||
export const ActionModifiers = {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -245,7 +245,7 @@ export function downloadItem(
|
||||
},
|
||||
"Cancel",
|
||||
undefined,
|
||||
container.getDownloadModalContent(name),
|
||||
container.getDownloadModalConent(name),
|
||||
);
|
||||
}
|
||||
export async function downloadNotebookItem(
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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({});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 || "";
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user