mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-23 10:51:30 +00:00
Compare commits
27 Commits
cloudshell
...
release/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7224dd26c1 | ||
|
|
4c73a1cc47 | ||
|
|
610da6a9a5 | ||
|
|
7812ca4914 | ||
|
|
bc4f18ba79 | ||
|
|
fd2551423d | ||
|
|
508abcd21c | ||
|
|
7774589d60 | ||
|
|
e23ba5ec8c | ||
|
|
75719b3cf0 | ||
|
|
f3f8fd241a | ||
|
|
2dc2e59162 | ||
|
|
6b811b5e76 | ||
|
|
69cf523274 | ||
|
|
2e45d8a2a4 | ||
|
|
8624bf0423 | ||
|
|
db1600d81b | ||
|
|
176bb47cb5 | ||
|
|
b6d17284b5 | ||
|
|
7b7a2817b6 | ||
|
|
f0e32491d7 | ||
|
|
c33c497fd9 | ||
|
|
cec621443d | ||
|
|
b8017763b7 | ||
|
|
59619a856e | ||
|
|
b1f016a796 | ||
|
|
ae3912cbf2 |
@@ -23,6 +23,8 @@ src/Common/MongoUtility.ts
|
|||||||
src/Common/NotificationsClientBase.ts
|
src/Common/NotificationsClientBase.ts
|
||||||
src/Common/QueriesClient.ts
|
src/Common/QueriesClient.ts
|
||||||
src/Common/Splitter.ts
|
src/Common/Splitter.ts
|
||||||
|
src/Controls/Heatmap/Heatmap.test.ts
|
||||||
|
src/Controls/Heatmap/Heatmap.ts
|
||||||
src/Definitions/datatables.d.ts
|
src/Definitions/datatables.d.ts
|
||||||
src/Definitions/gif.d.ts
|
src/Definitions/gif.d.ts
|
||||||
src/Definitions/globals.d.ts
|
src/Definitions/globals.d.ts
|
||||||
|
|||||||
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@@ -164,42 +164,24 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
|
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||||
shardTotal: [16]
|
shardTotal: [8]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: "Az CLI login"
|
||||||
|
uses: azure/login@v1
|
||||||
|
with:
|
||||||
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
- name: Use Node.js 18.x
|
- name: Use Node.js 18.x
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.x
|
node-version: 18.x
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npx playwright install --with-deps
|
- run: npx playwright install --with-deps
|
||||||
- name: "Az CLI login"
|
|
||||||
uses: Azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.E2E_TESTS_CLIENT_ID }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
# We can't use MSAL within playwright so we acquire tokens prior to running the tests
|
|
||||||
- name: "Acquire RBAC tokens for test accounts"
|
|
||||||
uses: azure/cli@v2
|
|
||||||
with:
|
|
||||||
azcliversion: latest
|
|
||||||
inlineScript: |
|
|
||||||
NOSQL_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-sql.documents.azure.com/.default" -o tsv --query accessToken)
|
|
||||||
echo "::add-mask::$NOSQL_TESTACCOUNT_TOKEN"
|
|
||||||
echo NOSQL_TESTACCOUNT_TOKEN=$NOSQL_TESTACCOUNT_TOKEN >> $GITHUB_ENV
|
|
||||||
NOSQL_READONLY_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-sql-readonly.documents.azure.com/.default" -o tsv --query accessToken)
|
|
||||||
echo "::add-mask::$NOSQL_READONLY_TESTACCOUNT_TOKEN"
|
|
||||||
echo NOSQL_READONLY_TESTACCOUNT_TOKEN=$NOSQL_READONLY_TESTACCOUNT_TOKEN >> $GITHUB_ENV
|
|
||||||
TABLE_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-tables.documents.azure.com/.default" -o tsv --query accessToken)
|
|
||||||
echo "::add-mask::$TABLE_TESTACCOUNT_TOKEN"
|
|
||||||
echo TABLE_TESTACCOUNT_TOKEN=$TABLE_TESTACCOUNT_TOKEN >> $GITHUB_ENV
|
|
||||||
GREMLIN_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-gremlin.documents.azure.com/.default" -o tsv --query accessToken)
|
|
||||||
echo "::add-mask::$GREMLIN_TESTACCOUNT_TOKEN"
|
|
||||||
echo GREMLIN_TESTACCOUNT_TOKEN=$GREMLIN_TESTACCOUNT_TOKEN >> $GITHUB_ENV
|
|
||||||
- name: Run test shard ${{ matrix['shardIndex'] }} of ${{ matrix['shardTotal']}}
|
- name: Run test shard ${{ matrix['shardIndex'] }} of ${{ matrix['shardTotal']}}
|
||||||
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --workers=3
|
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
- name: Upload blob report to GitHub Actions Artifacts
|
- name: Upload blob report to GitHub Actions Artifacts
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
2
.github/workflows/cleanup.yml
vendored
2
.github/workflows/cleanup.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
|||||||
- name: "Az CLI login"
|
- name: "Az CLI login"
|
||||||
uses: azure/login@v1
|
uses: azure/login@v1
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.E2E_TESTS_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
|
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
|
||||||
|
"isTerminalEnabled": true,
|
||||||
"isPhoenixEnabled": true
|
"isPhoenixEnabled": true
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
|
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
|
||||||
"isPhoenixEnabled": false
|
"isTerminalEnabled" : false,
|
||||||
|
"isPhoenixEnabled" : false
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 6.9 KiB |
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g fill="none">
|
|
||||||
<path fill="#8CC5E7" d="M21.4679537,3.20617761 C22.1814672,4.67953668 20.0131274,4.83706564 20.1243243,5.49498069 C20.3281853,6.68108108 20.1891892,8.44169884 20.0316602,10.1745174 C19.7629344,13.1119691 21.9590734,20.1451737 17.3814672,22.9714286 C16.5196911,23.5088803 14.4718147,23.8054054 12.4517375,23.8517375 C12.4517375,23.8517375 12.442471,23.8517375 12.442471,23.8517375 C12.442471,23.8517375 12.4332046,23.8517375 12.4332046,23.8517375 C10.4131274,23.8054054 8.08725869,23.5088803 7.22548263,22.9714286 C2.65714286,20.1451737 4.85328185,13.1119691 4.59382239,10.1745174 C4.42702703,8.44169884 4.28803089,6.68108108 4.5011583,5.49498069 C4.61235521,4.83706564 2.44401544,4.68880309 3.15752896,3.20617761 C3.76911197,1.93667954 5.27953668,3.05791506 5.65945946,2.65945946 C7.596139,0.648648649 9.94980695,0.111196911 11.8030888,0.0648648649 C11.988417,0.0648648649 12.8223938,0.0648648649 12.8223938,0.0648648649 C14.6664093,0.157528958 17.0200772,0.657915058 18.9660232,2.65945946 C19.3459459,3.05791506 20.8471042,1.93667954 21.4679537,3.20617761 Z M11.4324324,10.9065637 C11.3490347,10.9436293 11.2100386,11.8517375 11.6362934,11.8980695 C11.9235521,11.9258687 12.7111969,12.0185328 12.8965251,11.8980695 C13.2579151,11.6664093 13.2208494,11.1104247 13.0169884,10.9714286 C12.6741313,10.7490347 11.5250965,10.8602317 11.4324324,10.9065637 Z M9.07876448,4.10501931 C8.12432432,3.99382239 6.52123552,4.88339768 6.28030888,6.77374517 C6.02084942,8.73822394 8.33745174,10.6841699 10.56139,8.73822394 C11.7567568,7.69111969 12.1737452,4.46640927 9.07876448,4.10501931 Z M15.5281853,4.10501931 C12.4332046,4.46640927 12.8501931,7.69111969 14.0455598,8.73822394 C16.2694981,10.6841699 18.5861004,8.73822394 18.3266409,6.77374517 C18.0949807,4.88339768 16.4918919,3.99382239 15.5281853,4.10501931 Z"/>
|
|
||||||
<path fill="#B8937F" d="M12.3127413,8.98841699 C12.8965251,8.90501931 14.2957529,9.57220077 14.2030888,10.3598456 C14.0918919,11.2772201 10.5984556,11.3976834 10.4131274,10.3042471 C10.3019305,9.63706564 10.8301158,9.21081081 12.3127413,8.98841699 Z M20.1984556,16.3737452 C19.9111969,16.3644788 19.7258687,15.984556 19.7258687,15.7528958 C19.7258687,15.3359073 19.7814672,14.8447876 20.0872587,14.6316602 C20.7173745,14.196139 21.2177606,16.3830116 20.1984556,16.3737452 Z M4.41776062,16.3737452 C3.3984556,16.3830116 3.8988417,14.196139 4.52895753,14.6316602 C4.83474903,14.8447876 4.89034749,15.3359073 4.89034749,15.7528958 C4.89034749,15.984556 4.70501931,16.3644788 4.41776062,16.3737452 Z M18.2617761,23.0918919 C18.4471042,23.3606178 18.4563707,23.5459459 18.1598456,23.6849421 C17.0293436,24.203861 16.019305,23.5088803 16.3992278,23.3142857 C17.2054054,22.9065637 17.7057915,22.2671815 18.2617761,23.0918919 Z M6.35444015,23.184556 C6.91042471,22.3598456 7.41081081,22.9992278 8.21698842,23.4069498 C8.5969112,23.6015444 7.58687259,24.2965251 6.45637066,23.7776062 C6.15984556,23.63861 6.16911197,23.4532819 6.35444015,23.184556 Z"/>
|
|
||||||
<path fill="#000000" d="M19.7351351,3.42857143 C19.7814672,3.23397683 20.2633205,3.14131274 20.5320463,3.47490347 C20.8563707,3.87335907 20.0594595,4.42007722 20.0223938,4.1976834 C19.9297297,3.5953668 19.6795367,3.62316602 19.7351351,3.42857143 Z M4.88108108,3.42857143 C4.93667954,3.62316602 4.68648649,3.5953668 4.59382239,4.1976834 C4.55675676,4.42007722 3.75984556,3.87335907 4.08416988,3.47490347 C4.34362934,3.14131274 4.82548263,3.23397683 4.88108108,3.42857143 Z M15.7413127,7.94131274 C15.1578953,7.94131274 14.6849421,7.46835949 14.6849421,6.88494208 C14.6849421,6.30152468 15.1578953,5.82857143 15.7413127,5.82857143 C16.3247301,5.82857143 16.7976834,6.30152468 16.7976834,6.88494208 C16.7976834,7.46835949 16.3247301,7.94131274 15.7413127,7.94131274 Z M15.4633205,6.76447876 C15.6475575,6.76447876 15.7969112,6.61512511 15.7969112,6.43088803 C15.7969112,6.24665096 15.6475575,6.0972973 15.4633205,6.0972973 C15.2790834,6.0972973 15.1297297,6.24665096 15.1297297,6.43088803 C15.1297297,6.61512511 15.2790834,6.76447876 15.4633205,6.76447876 Z M11.3583012,9.43320463 C11.4694981,9.00694981 11.8586873,8.86795367 12.1737452,8.85868726 C12.9799228,8.84015444 13.2857143,9.27567568 13.3135135,9.61853282 C13.369112,10.2023166 11.1081081,10.3413127 11.3583012,9.43320463 Z M8.87490347,7.94131274 C8.29148607,7.94131274 7.81853282,7.46835949 7.81853282,6.88494208 C7.81853282,6.30152468 8.29148607,5.82857143 8.87490347,5.82857143 C9.45832088,5.82857143 9.93127413,6.30152468 9.93127413,6.88494208 C9.93127413,7.46835949 9.45832088,7.94131274 8.87490347,7.94131274 Z M9.15289575,6.76447876 C9.33713283,6.76447876 9.48648649,6.61512511 9.48648649,6.43088803 C9.48648649,6.24665096 9.33713283,6.0972973 9.15289575,6.0972973 C8.96865868,6.0972973 8.81930502,6.24665096 8.81930502,6.43088803 C8.81930502,6.61512511 8.96865868,6.76447876 9.15289575,6.76447876 Z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
<svg width="800px" height="800px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
|
||||||
<g>
|
|
||||||
<path d="M38.9437824,35.879008 C89.5234256,-13.1200214 170.398168,-11.8028432 219.397197,39.0402357 C224.929346,31.6640377 229.671187,23.4975328 233.095851,15.0675923 C249.165425,64.0666217 258.912543,105.162582 255.224444,137.038295 C253.380395,163.90873 242.842969,189.725423 225.456217,210.273403 C180.145286,264.014274 99.53398,270.863601 45.7931091,225.55267 L45.7931091,225.55267 L44.765,224.638 L44.7103323,224.601984 C44.5420247,224.484832 44.376007,224.362668 44.2124952,224.235492 C43.7219599,223.853965 43.2765312,223.438607 42.8762093,222.995252 L42.732,222.831 L41.0512675,221.3377 C39.4121124,219.93271 37.7729573,218.52772 36.3188215,216.93771 L35.7825547,216.332423 C-13.2164747,165.752779 -11.6358609,84.8780374 38.9437824,35.879008 Z M57.9111486,207.375611 C53.169307,203.687512 46.3199803,204.214383 42.6318814,208.956225 C39.3888978,213.125775 39.4048731,218.924805 42.6798072,222.771269 L42.732,222.831 L44.765,224.638 L44.9644841,224.773953 C49.5691585,227.80174 55.7644273,227.175885 59.2982065,222.896387 L59.4917624,222.654878 C63.1798614,217.913037 62.3895545,211.06371 57.9111486,207.375611 Z M231.778672,28.2393744 C218.60689,55.9001168 185.940871,76.9749681 157.753257,83.5608592 C131.146257,89.8833146 107.963921,84.6146018 83.4644059,94.0982849 C27.6160498,115.436572 28.6697923,181.822354 59.2283268,196.838185 L59.2283268,196.838185 L61.0723763,197.891928 C61.0723763,197.891928 83.1456487,193.50309 104.973663,187.707242 L106.843514,187.207079 C115.561826,184.857554 124.138869,182.296538 131.146257,179.714869 C167.500376,166.279651 207.542593,133.08676 220.714375,94.6251562 C213.865049,134.667374 179.35498,173.392413 144.84491,191.042601 C126.404416,200.526284 112.178891,202.633769 81.883792,213.171195 C78.195693,214.488373 75.297901,215.805551 75.297901,215.805551 C75.6675607,215.754564 76.0372203,215.70481 76.4060145,215.65629 L77.1421925,215.560893 L77.1421925,215.560893 L77.8745239,215.468787 C84.5652297,214.639554 90.5771682,214.224938 90.5771682,214.224938 C133.517178,212.117452 200.956702,226.342977 232.305544,184.45671 C264.444692,141.780136 246.531068,72.7599979 231.778672,28.2393744 Z" fill="#6DB33F">
|
|
||||||
|
|
||||||
</path>
|
|
||||||
<path d="M57.9111486,207.375611 C62.3895545,211.06371 63.1798614,217.913037 59.4917624,222.654878 C55.8036635,227.39672 48.9543368,227.923591 44.2124952,224.235492 C39.4706537,220.547393 38.9437824,213.698066 42.6318814,208.956225 C46.3199803,204.214383 53.169307,203.687512 57.9111486,207.375611 Z M231.778672,28.2393744 C246.531068,72.7599979 264.444692,141.780136 232.305544,184.45671 C200.956702,226.342977 133.517178,212.117452 90.5771682,214.224938 C90.5771682,214.224938 84.5652297,214.639554 77.8745239,215.468787 L77.1421925,215.560893 C76.5300999,215.63902 75.9140004,215.720572 75.297901,215.805551 C75.297901,215.805551 78.195693,214.488373 81.883792,213.171195 C112.178891,202.633769 126.404416,200.526284 144.84491,191.042601 C179.35498,173.392413 213.865049,134.667374 220.714375,94.6251562 C207.542593,133.08676 167.500376,166.279651 131.146257,179.714869 C106.119871,188.935116 61.0723763,197.891928 61.0723763,197.891928 L59.2283268,196.838185 C28.6697923,181.822354 27.6160498,115.436572 83.4644059,94.0982849 C107.963921,84.6146018 131.146257,89.8833146 157.753257,83.5608592 C185.940871,76.9749681 218.60689,55.9001168 231.778672,28.2393744 Z" fill="#FFFFFF">
|
|
||||||
|
|
||||||
</path>
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -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 |
@@ -1914,20 +1914,13 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs-margin {
|
.nav-tabs-margin {
|
||||||
|
height: 32px;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
|
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-bottom: -0.5px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
// Override the bootstrap defaults here to align with our layout constants.
|
|
||||||
margin-bottom: 0px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2869,7 +2862,6 @@ a:link {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: clip;
|
overflow-x: clip;
|
||||||
min-height: fit-content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.uniqueIndexesContainer {
|
.uniqueIndexesContainer {
|
||||||
|
|||||||
@@ -211,12 +211,3 @@ a:focus {
|
|||||||
.fileImportImg img {
|
.fileImportImg img {
|
||||||
filter: brightness(0) saturate(100%);
|
filter: brightness(0) saturate(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabPanesContainer {
|
|
||||||
overflow: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs-container {
|
|
||||||
min-height: 500px;
|
|
||||||
min-width: 500px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ body {
|
|||||||
|
|
||||||
.topSelected:hover {
|
.topSelected:hover {
|
||||||
border-left: 4px solid @AccentMediumHigh;
|
border-left: 4px solid @AccentMediumHigh;
|
||||||
background: #666666 !important;
|
background: #666666!important;
|
||||||
cursor: default !important;
|
cursor: default!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#Quickstart:hover span.activemenu,
|
#Quickstart:hover span.activemenu,
|
||||||
@@ -130,19 +130,19 @@ menuQuickStart {
|
|||||||
.content {
|
.content {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: all 0.4s ease-in-out;
|
transition: all .4s ease-in-out;
|
||||||
-ms-transition: all 0.4s ease-in-out;
|
-ms-transition: all .4s ease-in-out;
|
||||||
-webkit-transition: all 0.4s ease-in-out;
|
-webkit-transition: all .4s ease-in-out;
|
||||||
-moz-transition: all 0.4s ease-in-out;
|
-moz-transition: all .4s ease-in-out;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini {
|
.mini {
|
||||||
width: 0%;
|
width: 0%;
|
||||||
float: left;
|
float: left;
|
||||||
transition: all 0.4s ease-in-out;
|
transition: all .4s ease-in-out;
|
||||||
-webkit-transition: all 0.4s ease-in-out;
|
-webkit-transition: all .4s ease-in-out;
|
||||||
-moz-transition: all 0.4s ease-in-out;
|
-moz-transition: all .4s ease-in-out;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
@@ -226,39 +226,39 @@ menuQuickStart {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tbodycontent > tr > td {
|
#tbodycontent>tr>td {
|
||||||
border-bottom: 1px solid #cccccc;
|
border-bottom: 1px solid #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tbodycontent > tr:last-child > td {
|
#tbodycontent>tr:last-child>td {
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridRowSelected {
|
.gridRowSelected {
|
||||||
background-color: #def;
|
background-color: #DEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridRowSelected:hover {
|
.gridRowSelected:hover {
|
||||||
background-color: #def !important;
|
background-color: #DEF!important;
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collectionNodeSelected {
|
.collectionNodeSelected {
|
||||||
background-color: #def;
|
background-color: #DEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collectionNodeSelected:hover {
|
.collectionNodeSelected:hover {
|
||||||
background-color: #def !important;
|
background-color: #DEF!important;
|
||||||
cursor: default !important;
|
cursor: default!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.databaseNodeSelected {
|
.databaseNodeSelected {
|
||||||
background-color: #def;
|
background-color: #DEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.databaseNodeSelected:hover {
|
.databaseNodeSelected:hover {
|
||||||
background-color: #def !important;
|
background-color: #DEF!important;
|
||||||
cursor: default !important;
|
cursor: default!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leftsidepanle-hr {
|
.leftsidepanle-hr {
|
||||||
@@ -266,7 +266,7 @@ menuQuickStart {
|
|||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
margin-left: -17px;
|
margin-left: -17px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: 1px solid #53575b;
|
color: 1px solid #53575B;
|
||||||
}
|
}
|
||||||
|
|
||||||
.partitioning-btn {
|
.partitioning-btn {
|
||||||
@@ -328,7 +328,7 @@ menuQuickStart {
|
|||||||
|
|
||||||
.collid-white {
|
.collid-white {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: solid 1px #ddd;
|
border: solid 1px #DDD;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plusimg-but {
|
.plusimg-but {
|
||||||
@@ -515,11 +515,11 @@ label {
|
|||||||
|
|
||||||
.datalist-arrow:focus:after,
|
.datalist-arrow:focus:after,
|
||||||
.datalist-arrow:active:after {
|
.datalist-arrow:active:after {
|
||||||
background: #1ebbee;
|
background: #1EBBEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
input::-webkit-calendar-picker-indicator::after {
|
input::-webkit-calendar-picker-indicator::after {
|
||||||
content: "\276F";
|
content: '\276F';
|
||||||
right: 0;
|
right: 0;
|
||||||
top: -8%;
|
top: -8%;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -533,7 +533,7 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.datalist-arrow:after:hover {
|
.datalist-arrow:after:hover {
|
||||||
content: "\276F";
|
content: '\276F';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 1px;
|
right: 1px;
|
||||||
top: 6%;
|
top: 6%;
|
||||||
@@ -545,7 +545,7 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-color: #1ebbee;
|
background-color: #1EBBEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Introline3 {
|
.Introline3 {
|
||||||
@@ -567,7 +567,7 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.collectionCollapsed:hover {
|
.collectionCollapsed:hover {
|
||||||
background: #eeeeee;
|
background: #EEEEEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collectionCollapsed:active {
|
.collectionCollapsed:active {
|
||||||
@@ -590,12 +590,12 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
|
|
||||||
.leftarrowCollapsed {
|
.leftarrowCollapsed {
|
||||||
padding: 2px 4px 4px 5px;
|
padding: 2px 4px 4px 5px;
|
||||||
border: solid 1px #fff;
|
border: solid 1px #FFF;
|
||||||
margin: 6px 4px 0px -11px;
|
margin: 6px 4px 0px -11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leftarrowCollapsed:hover {
|
.leftarrowCollapsed:hover {
|
||||||
background-color: #eeeeee;
|
background-color: #EEEEEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leftarrowCollapsed:active {
|
.leftarrowCollapsed:active {
|
||||||
@@ -635,12 +635,12 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numberheading > p {
|
.numberheading>p {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numberheading > ul {
|
.numberheading>ul {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@@ -650,12 +650,16 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numberheading > ul > li > a {
|
.numberheading>ul>li>a {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #0058ad;
|
color: #0058ad;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sampleApp {
|
.netApp {
|
||||||
|
padding-bottom: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pythonApp {
|
||||||
padding-bottom: 45px;
|
padding-bottom: 45px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +667,7 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
padding-bottom: 110px;
|
padding-bottom: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step1 > input {
|
.step1>input {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +691,7 @@ input::-webkit-calendar-picker-indicator::after {
|
|||||||
.atags {
|
.atags {
|
||||||
color: @AccentMediumHigh;
|
color: @AccentMediumHigh;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
cursor: pointer;
|
cursor: pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
.qsmenuicons {
|
.qsmenuicons {
|
||||||
@@ -750,13 +754,13 @@ a:link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.command:hover {
|
.command:hover {
|
||||||
background-color: #e6e6e6;
|
background-color: #E6E6E6;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command:active {
|
.command:active {
|
||||||
background-color: #cccccc;
|
background-color: #CCCCCC;
|
||||||
border: solid 1px @AccentMediumHigh;
|
border: solid 1px @AccentMediumHigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -766,7 +770,7 @@ a:link {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav > li > a:focus {
|
.nav>li>a:focus {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,7 +895,7 @@ a:link {
|
|||||||
padding: 4px 4px -1px 0px;
|
padding: 4px 4px -1px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#divcontent > .mongoDocumentEditor .monaco-editor.vs .redsquiggly {
|
#divcontent>.mongoDocumentEditor .monaco-editor.vs .redsquiggly {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,7 +933,7 @@ td a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-fixed tbody td,
|
.table-fixed tbody td,
|
||||||
.table-fixed thead > tr > th {
|
.table-fixed thead>tr>th {
|
||||||
float: left;
|
float: left;
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
}
|
}
|
||||||
@@ -962,7 +966,7 @@ a:link {
|
|||||||
color: #393939;
|
color: #393939;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"] {
|
.tab [type=radio] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,40 +979,40 @@ a:link {
|
|||||||
padding: 15px 0px 20px 0;
|
padding: 15px 0px 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"]:checked ~ label {
|
.tab [type=radio]:checked~label {
|
||||||
border: 1px solid #0072c6;
|
border: 1px solid #0072c6;
|
||||||
background-color: @AccentMediumHigh;
|
background-color: @AccentMediumHigh;
|
||||||
color: white;
|
color: white;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"]:checked ~ label:hover {
|
.tab [type=radio]:checked~label:hover {
|
||||||
border: 1px solid @AccentMediumHigh;
|
border: 1px solid @AccentMediumHigh;
|
||||||
background-color: @AccentMediumHigh;
|
background-color: @AccentMediumHigh;
|
||||||
color: white;
|
color: white;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"]:checked ~ label:active {
|
.tab [type=radio]:checked~label:active {
|
||||||
border: 1px solid #0072c6;
|
border: 1px solid #0072c6;
|
||||||
background-color: #0072c6;
|
background-color: #0072c6;
|
||||||
color: white;
|
color: white;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"]:checked ~ label ~ .tabcontent {
|
.tab [type=radio]:checked~label~.tabcontent {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"]:not(:checked) ~ label:hover {
|
.tab [type=radio]:not(:checked)~label:hover {
|
||||||
border: 1px solid #969696;
|
border: 1px solid #969696;
|
||||||
background-color: #969696;
|
background-color: #969696;
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab [type="radio"]:not(:checked) ~ label ~ .tabcontent {
|
.tab [type=radio]:not(:checked)~label~.tabcontent {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1038,7 +1042,7 @@ a:link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.atagdetails {
|
.atagdetails {
|
||||||
padding-left: 55px !important;
|
padding-left: 55px!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.path {
|
.path {
|
||||||
@@ -1176,7 +1180,7 @@ a:link {
|
|||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs > li > a {
|
.nav-tabs>li>a {
|
||||||
border-radius: 2px 2px 0 0;
|
border-radius: 2px 2px 0 0;
|
||||||
padding: 8px 0px 4px 0px;
|
padding: 8px 0px 4px 0px;
|
||||||
color: #393939;
|
color: #393939;
|
||||||
@@ -1186,10 +1190,10 @@ a:link {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs > li.active > a,
|
.nav-tabs>li.active>a,
|
||||||
.nav-tabs > li.active > a:focus,
|
.nav-tabs>li.active>a:focus,
|
||||||
.nav-tabs > li.active > a:hover {
|
.nav-tabs>li.active>a:hover {
|
||||||
border-bottom-color: #fff;
|
border-bottom-color: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabList {
|
.tabList {
|
||||||
@@ -1234,12 +1238,12 @@ a:link {
|
|||||||
padding: 2px 12px 0 13px;
|
padding: 2px 12px 0 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs > li > a:active {
|
.nav-tabs>li>a:active {
|
||||||
background-color: #e0e0e0;
|
background-color: #e0e0e0;
|
||||||
border-color: @AccentMediumHigh;
|
border-color: @AccentMediumHigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tabs > li > a:active .tab_Content {
|
.nav-tabs>li>a:active .tab_Content {
|
||||||
border: transparent;
|
border: transparent;
|
||||||
width: 130px;
|
width: 130px;
|
||||||
}
|
}
|
||||||
@@ -1258,7 +1262,7 @@ a:link {
|
|||||||
|
|
||||||
.clickableLink {
|
.clickableLink {
|
||||||
color: @AccentMediumHigh;
|
color: @AccentMediumHigh;
|
||||||
font-family: "Segoe UI";
|
font-family: 'Segoe UI';
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -1288,7 +1292,7 @@ a:link {
|
|||||||
width: 86px;
|
width: 86px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.codeblock {
|
input.codeblock{
|
||||||
background-color: @BaseMediumLow;
|
background-color: @BaseMediumLow;
|
||||||
color: #252525;
|
color: #252525;
|
||||||
border: 1px solid @BaseMediumHigh;
|
border: 1px solid @BaseMediumHigh;
|
||||||
@@ -1302,6 +1306,6 @@ input.codeblock {
|
|||||||
cursor: text;
|
cursor: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
#divQuickStartConnections {
|
#divQuickStartConnections{
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
304
package-lock.json
generated
304
package-lock.json
generated
@@ -10,7 +10,7 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/arm-cosmosdb": "9.1.0",
|
"@azure/arm-cosmosdb": "9.1.0",
|
||||||
"@azure/cosmos": "4.5.0",
|
"@azure/cosmos": "4.2.0-beta.1",
|
||||||
"@azure/cosmos-language-service": "0.0.5",
|
"@azure/cosmos-language-service": "0.0.5",
|
||||||
"@azure/identity": "4.5.0",
|
"@azure/identity": "4.5.0",
|
||||||
"@azure/msal-browser": "2.14.2",
|
"@azure/msal-browser": "2.14.2",
|
||||||
@@ -51,8 +51,6 @@
|
|||||||
"@types/mkdirp": "1.0.1",
|
"@types/mkdirp": "1.0.1",
|
||||||
"@types/node-fetch": "2.5.7",
|
"@types/node-fetch": "2.5.7",
|
||||||
"@xmldom/xmldom": "0.7.13",
|
"@xmldom/xmldom": "0.7.13",
|
||||||
"@xterm/addon-fit": "0.10.0",
|
|
||||||
"@xterm/xterm": "5.5.0",
|
|
||||||
"allotment": "1.20.2",
|
"allotment": "1.20.2",
|
||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
@@ -88,7 +86,7 @@
|
|||||||
"mkdirp": "1.0.4",
|
"mkdirp": "1.0.4",
|
||||||
"monaco-editor": "0.44.0",
|
"monaco-editor": "0.44.0",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"p-retry": "6.2.1",
|
"p-retry": "4.6.2",
|
||||||
"patch-package": "8.0.0",
|
"patch-package": "8.0.0",
|
||||||
"plotly.js-cartesian-dist-min": "1.52.3",
|
"plotly.js-cartesian-dist-min": "1.52.3",
|
||||||
"post-robot": "10.0.42",
|
"post-robot": "10.0.42",
|
||||||
@@ -290,71 +288,59 @@
|
|||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/@azure/core-http-compat": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/abort-controller": "^2.0.0",
|
|
||||||
"@azure/core-client": "^1.3.0",
|
|
||||||
"@azure/core-rest-pipeline": "^1.20.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@azure/core-lro": {
|
|
||||||
"version": "2.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz",
|
|
||||||
"integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/abort-controller": "^2.0.0",
|
|
||||||
"@azure/core-util": "^1.2.0",
|
|
||||||
"@azure/logger": "^1.0.0",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@azure/core-lro/node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
|
||||||
"node_modules/@azure/core-paging": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz",
|
|
||||||
"integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@azure/core-paging/node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
|
||||||
"node_modules/@azure/core-rest-pipeline": {
|
"node_modules/@azure/core-rest-pipeline": {
|
||||||
"version": "1.20.0",
|
"version": "1.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz",
|
||||||
"integrity": "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g==",
|
"integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/abort-controller": "^2.0.0",
|
"@azure/abort-controller": "^2.0.0",
|
||||||
"@azure/core-auth": "^1.8.0",
|
"@azure/core-auth": "^1.8.0",
|
||||||
"@azure/core-tracing": "^1.0.1",
|
"@azure/core-tracing": "^1.0.1",
|
||||||
"@azure/core-util": "^1.11.0",
|
"@azure/core-util": "^1.11.0",
|
||||||
"@azure/logger": "^1.0.0",
|
"@azure/logger": "^1.0.0",
|
||||||
"@typespec/ts-http-runtime": "^0.2.2",
|
"http-proxy-agent": "^7.0.0",
|
||||||
|
"https-proxy-agent": "^7.0.0",
|
||||||
"tslib": "^2.6.2"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@azure/core-rest-pipeline/node_modules/agent-base": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.3.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-rest-pipeline/node_modules/http-proxy-agent": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "^7.1.0",
|
||||||
|
"debug": "^4.3.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "^7.0.2",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@azure/core-rest-pipeline/node_modules/tslib": {
|
"node_modules/@azure/core-rest-pipeline/node_modules/tslib": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
@@ -391,25 +377,23 @@
|
|||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/@azure/cosmos": {
|
"node_modules/@azure/cosmos": {
|
||||||
"version": "4.5.0",
|
"version": "4.2.0-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz",
|
||||||
"integrity": "sha512-JsTh4twb6FcwP7rJwxQiNZQ/LGtuF6gmciaxY9Rnp6/A325Lhsw/SH4R2ArpT0yCvozbZpweIwdPfUkXVBtp5w==",
|
"integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/abort-controller": "^2.1.2",
|
"@azure/abort-controller": "^2.0.0",
|
||||||
"@azure/core-auth": "^1.9.0",
|
"@azure/core-auth": "^1.7.1",
|
||||||
"@azure/core-rest-pipeline": "^1.19.1",
|
"@azure/core-rest-pipeline": "^1.15.1",
|
||||||
"@azure/core-tracing": "^1.2.0",
|
"@azure/core-tracing": "^1.1.1",
|
||||||
"@azure/core-util": "^1.11.0",
|
"@azure/core-util": "^1.8.1",
|
||||||
"@azure/keyvault-keys": "^4.9.0",
|
|
||||||
"@azure/logger": "^1.1.4",
|
|
||||||
"fast-json-stable-stringify": "^2.1.0",
|
"fast-json-stable-stringify": "^2.1.0",
|
||||||
|
"jsbi": "^4.3.0",
|
||||||
"priorityqueuejs": "^2.0.0",
|
"priorityqueuejs": "^2.0.0",
|
||||||
"semaphore": "^1.1.0",
|
"semaphore": "^1.1.0",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.6.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@azure/cosmos-language-service": {
|
"node_modules/@azure/cosmos-language-service": {
|
||||||
@@ -439,9 +423,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@azure/cosmos/node_modules/tslib": {
|
"node_modules/@azure/cosmos/node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"license": "0BSD"
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
},
|
||||||
"node_modules/@azure/identity": {
|
"node_modules/@azure/identity": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
@@ -507,66 +490,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||||
},
|
},
|
||||||
"node_modules/@azure/keyvault-common": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/abort-controller": "^2.0.0",
|
|
||||||
"@azure/core-auth": "^1.3.0",
|
|
||||||
"@azure/core-client": "^1.5.0",
|
|
||||||
"@azure/core-rest-pipeline": "^1.8.0",
|
|
||||||
"@azure/core-tracing": "^1.0.0",
|
|
||||||
"@azure/core-util": "^1.10.0",
|
|
||||||
"@azure/logger": "^1.1.4",
|
|
||||||
"tslib": "^2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@azure/keyvault-common/node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
|
||||||
"node_modules/@azure/keyvault-keys": {
|
|
||||||
"version": "4.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.9.0.tgz",
|
|
||||||
"integrity": "sha512-ZBP07+K4Pj3kS4TF4XdkqFcspWwBHry3vJSOFM5k5ZABvf7JfiMonvaFk2nBF6xjlEbMpz5PE1g45iTMme0raQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/abort-controller": "^2.0.0",
|
|
||||||
"@azure/core-auth": "^1.3.0",
|
|
||||||
"@azure/core-client": "^1.5.0",
|
|
||||||
"@azure/core-http-compat": "^2.0.1",
|
|
||||||
"@azure/core-lro": "^2.2.0",
|
|
||||||
"@azure/core-paging": "^1.1.1",
|
|
||||||
"@azure/core-rest-pipeline": "^1.8.1",
|
|
||||||
"@azure/core-tracing": "^1.0.0",
|
|
||||||
"@azure/core-util": "^1.0.0",
|
|
||||||
"@azure/keyvault-common": "^2.0.0",
|
|
||||||
"@azure/logger": "^1.0.0",
|
|
||||||
"tslib": "^2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@azure/keyvault-keys/node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
|
||||||
"node_modules/@azure/logger": {
|
"node_modules/@azure/logger": {
|
||||||
"version": "1.2.0",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.2.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-0hKEzLhpw+ZTAfNJyRrn6s+V0nDWzXk9OjBr2TiGIu0OfMr5s2V4FpKLTAK3Ca5r5OKLbf4hkOGDPyiRjie/jA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typespec/ts-http-runtime": "^0.2.2",
|
"tslib": "^2.2.0"
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@azure/logger/node_modules/tslib": {
|
"node_modules/@azure/logger/node_modules/tslib": {
|
||||||
@@ -12731,9 +12662,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/retry": {
|
"node_modules/@types/retry": {
|
||||||
"version": "0.12.2",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
|
|
||||||
"integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/sanitize-html": {
|
"node_modules/@types/sanitize-html": {
|
||||||
@@ -13141,56 +13070,6 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typespec/ts-http-runtime": {
|
|
||||||
"version": "0.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.2.2.tgz",
|
|
||||||
"integrity": "sha512-Gz/Sm64+Sq/vklJu1tt9t+4R2lvnud8NbTD/ZfpZtMiUX7YeVpCA8j6NSW8ptwcoLL+NmYANwqP8DV0q/bwl2w==",
|
|
||||||
"dependencies": {
|
|
||||||
"http-proxy-agent": "^7.0.0",
|
|
||||||
"https-proxy-agent": "^7.0.0",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@typespec/ts-http-runtime/node_modules/agent-base": {
|
|
||||||
"version": "7.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
|
||||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@typespec/ts-http-runtime/node_modules/http-proxy-agent": {
|
|
||||||
"version": "7.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
|
||||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
|
||||||
"dependencies": {
|
|
||||||
"agent-base": "^7.1.0",
|
|
||||||
"debug": "^4.3.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": {
|
|
||||||
"version": "7.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
|
||||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
|
||||||
"dependencies": {
|
|
||||||
"agent-base": "^7.1.2",
|
|
||||||
"debug": "4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@typespec/ts-http-runtime/node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
|
||||||
"node_modules/@ungap/url-search-params": {
|
"node_modules/@ungap/url-search-params": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
@@ -13359,19 +13238,6 @@
|
|||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@xterm/addon-fit": {
|
|
||||||
"version": "0.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
|
|
||||||
"integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@xterm/xterm": "^5.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@xterm/xterm": {
|
|
||||||
"version": "5.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
|
||||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
|
|
||||||
},
|
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
@@ -21933,18 +21799,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-network-error": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-number": {
|
"node_modules/is-number": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -27180,6 +27034,11 @@
|
|||||||
"js-yaml": "bin/js-yaml.js"
|
"js-yaml": "bin/js-yaml.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsbi": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g=="
|
||||||
|
},
|
||||||
"node_modules/jsbn": {
|
"node_modules/jsbn": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@@ -30384,20 +30243,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/p-retry": {
|
"node_modules/p-retry": {
|
||||||
"version": "6.2.1",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/retry": "0.12.2",
|
"@types/retry": "0.12.0",
|
||||||
"is-network-error": "^1.0.0",
|
|
||||||
"retry": "^0.13.1"
|
"retry": "^0.13.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.17"
|
"node": ">=8"
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/p-try": {
|
"node_modules/p-try": {
|
||||||
@@ -36144,13 +35997,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webpack-dev-server/node_modules/@types/retry": {
|
|
||||||
"version": "0.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
|
||||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/webpack-dev-server/node_modules/ajv": {
|
"node_modules/webpack-dev-server/node_modules/ajv": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -36198,20 +36044,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webpack-dev-server/node_modules/p-retry": {
|
|
||||||
"version": "4.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
|
|
||||||
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/retry": "0.12.0",
|
|
||||||
"retry": "^0.13.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/webpack-dev-server/node_modules/rimraf": {
|
"node_modules/webpack-dev-server/node_modules/rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/arm-cosmosdb": "9.1.0",
|
"@azure/arm-cosmosdb": "9.1.0",
|
||||||
"@azure/cosmos": "4.5.0",
|
"@azure/cosmos": "4.2.0-beta.1",
|
||||||
"@azure/cosmos-language-service": "0.0.5",
|
"@azure/cosmos-language-service": "0.0.5",
|
||||||
"@azure/identity": "4.5.0",
|
"@azure/identity": "4.5.0",
|
||||||
"@azure/msal-browser": "2.14.2",
|
"@azure/msal-browser": "2.14.2",
|
||||||
@@ -46,8 +46,6 @@
|
|||||||
"@types/mkdirp": "1.0.1",
|
"@types/mkdirp": "1.0.1",
|
||||||
"@types/node-fetch": "2.5.7",
|
"@types/node-fetch": "2.5.7",
|
||||||
"@xmldom/xmldom": "0.7.13",
|
"@xmldom/xmldom": "0.7.13",
|
||||||
"@xterm/xterm": "5.5.0",
|
|
||||||
"@xterm/addon-fit": "0.10.0",
|
|
||||||
"allotment": "1.20.2",
|
"allotment": "1.20.2",
|
||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
@@ -83,7 +81,7 @@
|
|||||||
"mkdirp": "1.0.4",
|
"mkdirp": "1.0.4",
|
||||||
"monaco-editor": "0.44.0",
|
"monaco-editor": "0.44.0",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"p-retry": "6.2.1",
|
"p-retry": "4.6.2",
|
||||||
"patch-package": "8.0.0",
|
"patch-package": "8.0.0",
|
||||||
"plotly.js-cartesian-dist-min": "1.52.3",
|
"plotly.js-cartesian-dist-min": "1.52.3",
|
||||||
"post-robot": "10.0.42",
|
"post-robot": "10.0.42",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig, devices } from "@playwright/test";
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
@@ -28,60 +29,24 @@ export default defineConfig({
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: "chromium",
|
name: "chromium",
|
||||||
use: {
|
use: { ...devices["Desktop Chrome"] },
|
||||||
...devices["Desktop Chrome"],
|
|
||||||
launchOptions: {
|
|
||||||
args: ["--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "firefox",
|
name: "firefox",
|
||||||
use: {
|
use: { ...devices["Desktop Firefox"] },
|
||||||
...devices["Desktop Firefox"],
|
|
||||||
launchOptions: {
|
|
||||||
firefoxUserPrefs: {
|
|
||||||
"security.fileuri.strict_origin_policy": false,
|
|
||||||
"network.http.referer.XOriginPolicy": 0,
|
|
||||||
"network.http.referer.trimmingPolicy": 0,
|
|
||||||
"privacy.file_unique_origin": false,
|
|
||||||
"security.csp.enable": false,
|
|
||||||
"network.cors_preflight.allow_client_cert": true,
|
|
||||||
"dom.security.https_first": false,
|
|
||||||
"network.http.cross-origin-embedder-policy": false,
|
|
||||||
"network.http.cross-origin-opener-policy": false,
|
|
||||||
"browser.tabs.remote.useCrossOriginPolicy": false,
|
|
||||||
"browser.tabs.remote.useCORP": false,
|
|
||||||
},
|
|
||||||
args: ["--disable-web-security"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "webkit",
|
name: "webkit",
|
||||||
use: {
|
use: { ...devices["Desktop Safari"] },
|
||||||
...devices["Desktop Safari"],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
/* Test against branded browsers. */
|
||||||
{
|
{
|
||||||
name: "Google Chrome",
|
name: "Google Chrome",
|
||||||
use: {
|
use: { ...devices["Desktop Chrome"], channel: "chrome" }, // or 'chrome-beta'
|
||||||
...devices["Desktop Chrome"],
|
|
||||||
channel: "chrome",
|
|
||||||
launchOptions: {
|
|
||||||
args: ["--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Microsoft Edge",
|
name: "Microsoft Edge",
|
||||||
use: {
|
use: { ...devices["Desktop Edge"], channel: "msedge" }, // or 'msedge-dev'
|
||||||
...devices["Desktop Edge"],
|
|
||||||
channel: "msedge",
|
|
||||||
launchOptions: {
|
|
||||||
args: ["--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
37045
preview/package-lock.json
generated
37045
preview/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -138,6 +138,15 @@ export enum MongoBackendEndpointType {
|
|||||||
remote,
|
remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class BackendApi {
|
||||||
|
public static readonly GenerateToken: string = "GenerateToken";
|
||||||
|
public static readonly PortalSettings: string = "PortalSettings";
|
||||||
|
public static readonly AccountRestrictions: string = "AccountRestrictions";
|
||||||
|
public static readonly RuntimeProxy: string = "RuntimeProxy";
|
||||||
|
public static readonly DisallowedLocations: string = "DisallowedLocations";
|
||||||
|
public static readonly SampleData: string = "SampleData";
|
||||||
|
}
|
||||||
|
|
||||||
export class PortalBackendEndpoints {
|
export class PortalBackendEndpoints {
|
||||||
public static readonly Development: string = "https://localhost:7235";
|
public static readonly Development: string = "https://localhost:7235";
|
||||||
public static readonly Mpac: string = "https://cdb-ms-mpac-pbe.cosmos.azure.com";
|
public static readonly Mpac: string = "https://cdb-ms-mpac-pbe.cosmos.azure.com";
|
||||||
@@ -248,7 +257,6 @@ export class Areas {
|
|||||||
public static ShareDialog: string = "Share Access Dialog";
|
public static ShareDialog: string = "Share Access Dialog";
|
||||||
public static Notebook: string = "Notebook";
|
public static Notebook: string = "Notebook";
|
||||||
public static Copilot: string = "Copilot";
|
public static Copilot: string = "Copilot";
|
||||||
public static CloudShell: string = "Cloud Shell";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HttpHeaders {
|
export class HttpHeaders {
|
||||||
@@ -522,8 +530,8 @@ export class ariaLabelForLearnMoreLink {
|
|||||||
public static readonly AzureSynapseLink = "Learn more about Azure Synapse Link.";
|
public static readonly AzureSynapseLink = "Learn more about Azure Synapse Link.";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GlobalSecondaryIndexLabels {
|
export class MaterializedViewsLabels {
|
||||||
public static readonly NewGlobalSecondaryIndex: string = "New Global Secondary Index";
|
public static readonly NewMaterializedView: string = "New Materialized View";
|
||||||
}
|
}
|
||||||
export class FeedbackLabels {
|
export class FeedbackLabels {
|
||||||
public static readonly provideFeedback: string = "Provide feedback";
|
public static readonly provideFeedback: string = "Provide feedback";
|
||||||
@@ -765,10 +773,3 @@ export const ShortenedQueryCopilotSampleContainerSchema = {
|
|||||||
|
|
||||||
userPrompt: "find all products",
|
userPrompt: "find all products",
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum MongoGuidRepresentation {
|
|
||||||
Standard = "Standard",
|
|
||||||
CSharpLegacy = "CSharpLegacy",
|
|
||||||
JavaLegacy = "JavaLegacy",
|
|
||||||
PythonLegacy = "PythonLegacy",
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract";
|
|||||||
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
||||||
import { checkDatabaseResourceTokensValidity, isFabricMirroredKey } from "Platform/Fabric/FabricUtil";
|
import { checkDatabaseResourceTokensValidity, isFabricMirroredKey } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { useDataplaneRbacAuthorization } from "Utils/AuthorizationUtils";
|
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { PriorityLevel } from "../Common/Constants";
|
import { PriorityLevel } from "../Common/Constants";
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
import { Platform, configContext } from "../ConfigContext";
|
import { Platform, configContext } from "../ConfigContext";
|
||||||
import { FabricArtifactInfo, updateUserContext, userContext } from "../UserContext";
|
import { FabricArtifactInfo, updateUserContext, userContext } from "../UserContext";
|
||||||
|
import { isDataplaneRbacSupported } from "../Utils/APITypeUtils";
|
||||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||||
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
||||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||||
@@ -20,7 +20,8 @@ const _global = typeof self === "undefined" ? window : self;
|
|||||||
export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
||||||
const { verb, resourceId, resourceType, headers } = requestInfo;
|
const { verb, resourceId, resourceType, headers } = requestInfo;
|
||||||
|
|
||||||
if (useDataplaneRbacAuthorization(userContext)) {
|
const dataPlaneRBACOptionEnabled = userContext.dataPlaneRbacEnabled && isDataplaneRbacSupported(userContext.apiType);
|
||||||
|
if (userContext.features.enableAadDataPlane || dataPlaneRBACOptionEnabled) {
|
||||||
Logger.logInfo(
|
Logger.logInfo(
|
||||||
`AAD Data Plane Feature flag set to ${userContext.features.enableAadDataPlane} for account with disable local auth ${userContext.databaseAccount.properties.disableLocalAuth} `,
|
`AAD Data Plane Feature flag set to ${userContext.features.enableAadDataPlane} for account with disable local auth ${userContext.databaseAccount.properties.disableLocalAuth} `,
|
||||||
"Explorer/tokenProvider",
|
"Explorer/tokenProvider",
|
||||||
@@ -124,11 +125,7 @@ export const endpoint = () => {
|
|||||||
const location = _global.parent ? _global.parent.location : _global.location;
|
const location = _global.parent ? _global.parent.location : _global.location;
|
||||||
return configContext.EMULATOR_ENDPOINT || location.origin;
|
return configContext.EMULATOR_ENDPOINT || location.origin;
|
||||||
}
|
}
|
||||||
return (
|
return userContext.endpoint || userContext?.databaseAccount?.properties?.documentEndpoint;
|
||||||
userContext.selectedRegionalEndpoint ||
|
|
||||||
userContext.endpoint ||
|
|
||||||
userContext?.databaseAccount?.properties?.documentEndpoint
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getTokenFromAuthService(
|
export async function getTokenFromAuthService(
|
||||||
@@ -206,7 +203,6 @@ export function client(): Cosmos.CosmosClient {
|
|||||||
userAgentSuffix: "Azure Portal",
|
userAgentSuffix: "Azure Portal",
|
||||||
defaultHeaders: _defaultHeaders,
|
defaultHeaders: _defaultHeaders,
|
||||||
connectionPolicy: {
|
connectionPolicy: {
|
||||||
enableEndpointDiscovery: !userContext.selectedRegionalEndpoint,
|
|
||||||
retryOptions: {
|
retryOptions: {
|
||||||
maxRetryAttemptCount: LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts),
|
maxRetryAttemptCount: LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts),
|
||||||
fixedRetryIntervalInMilliseconds: LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval),
|
fixedRetryIntervalInMilliseconds: LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { TagNames, WorkloadType } from "Common/Constants";
|
import { TagNames, WorkloadType } from "Common/Constants";
|
||||||
import { Tags } from "Contracts/DataModels";
|
import { Tags } from "Contracts/DataModels";
|
||||||
import { isFabric } from "Platform/Fabric/FabricUtil";
|
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
|
|
||||||
function isVirtualNetworkFilterEnabled() {
|
function isVirtualNetworkFilterEnabled() {
|
||||||
@@ -28,8 +27,6 @@ export function getWorkloadType(): WorkloadType {
|
|||||||
return workloadType;
|
return workloadType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isGlobalSecondaryIndexEnabled(): boolean {
|
export function isMaterializedViewsEnabled(): boolean {
|
||||||
return (
|
return userContext.apiType === "SQL" && userContext.databaseAccount?.properties?.enableMaterializedViews;
|
||||||
!isFabric() && userContext.apiType === "SQL" && userContext.databaseAccount?.properties?.enableMaterializedViews
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { QueryOperationOptions } from "@azure/cosmos";
|
||||||
import * as Constants from "../Common/Constants";
|
|
||||||
import { QueryResults } from "../Contracts/ViewModels";
|
import { QueryResults } from "../Contracts/ViewModels";
|
||||||
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
|
|
||||||
interface QueryResponse {
|
interface QueryResponse {
|
||||||
// [Todo] remove any
|
// [Todo] remove any
|
||||||
@@ -13,15 +11,17 @@ interface QueryResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MinimalQueryIterator {
|
export interface MinimalQueryIterator {
|
||||||
fetchNext: () => Promise<QueryResponse>;
|
fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick<QueryIterator<any>, "fetchNext">;
|
// Pick<QueryIterator<any>, "fetchNext">;
|
||||||
|
|
||||||
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
export function nextPage(
|
||||||
TelemetryProcessor.traceStart(Action.ExecuteQuery);
|
documentsIterator: MinimalQueryIterator,
|
||||||
return documentsIterator.fetchNext().then((response) => {
|
firstItemIndex: number,
|
||||||
TelemetryProcessor.traceSuccess(Action.ExecuteQuery, { dataExplorerArea: Constants.Areas.Tab });
|
queryOperationOptions?: QueryOperationOptions,
|
||||||
|
): Promise<QueryResults> {
|
||||||
|
return documentsIterator.fetchNext(queryOperationOptions).then((response) => {
|
||||||
const documents = response.resources;
|
const documents = response.resources;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -83,6 +84,7 @@ describe("MongoProxyClient", () => {
|
|||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
queryDocuments(databaseId, collection, true, "{}");
|
queryDocuments(databaseId, collection, true, "{}");
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
@@ -99,6 +101,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -117,6 +120,7 @@ describe("MongoProxyClient", () => {
|
|||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
readDocument(databaseId, collection, documentId);
|
readDocument(databaseId, collection, documentId);
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
@@ -133,6 +137,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -151,6 +156,7 @@ describe("MongoProxyClient", () => {
|
|||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
readDocument(databaseId, collection, documentId);
|
readDocument(databaseId, collection, documentId);
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
@@ -167,6 +173,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -190,6 +197,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -208,6 +216,7 @@ describe("MongoProxyClient", () => {
|
|||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
MONGO_PROXY_ENDPOINT: "https://localhost:1234",
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
deleteDocuments(databaseId, collection, [documentId]);
|
deleteDocuments(databaseId, collection, [documentId]);
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
@@ -224,6 +233,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
||||||
import { getMongoGuidRepresentation } from "Shared/StorageUtility";
|
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { configContext } from "../ConfigContext";
|
import { configContext } from "../ConfigContext";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
@@ -140,9 +139,6 @@ export function readDocument(
|
|||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
? documentId.partitionKeyProperties?.[0]
|
? documentId.partitionKeyProperties?.[0]
|
||||||
: "",
|
: "",
|
||||||
clientSettings: {
|
|
||||||
guidRepresentation: getMongoGuidRepresentation(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
||||||
@@ -185,9 +181,6 @@ export function createDocument(
|
|||||||
partitionKey:
|
partitionKey:
|
||||||
collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||||
documentContent: JSON.stringify(documentContent),
|
documentContent: JSON.stringify(documentContent),
|
||||||
clientSettings: {
|
|
||||||
guidRepresentation: getMongoGuidRepresentation(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
||||||
@@ -235,9 +228,6 @@ export function updateDocument(
|
|||||||
? documentId.partitionKeyProperties?.[0]
|
? documentId.partitionKeyProperties?.[0]
|
||||||
: "",
|
: "",
|
||||||
documentContent,
|
documentContent,
|
||||||
clientSettings: {
|
|
||||||
guidRepresentation: getMongoGuidRepresentation(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
||||||
|
|
||||||
@@ -284,9 +274,6 @@ export function deleteDocuments(
|
|||||||
subscriptionID: userContext.subscriptionId,
|
subscriptionID: userContext.subscriptionId,
|
||||||
resourceGroup: userContext.resourceGroup,
|
resourceGroup: userContext.resourceGroup,
|
||||||
databaseAccountName: databaseAccount.name,
|
databaseAccountName: databaseAccount.name,
|
||||||
clientSettings: {
|
|
||||||
guidRepresentation: getMongoGuidRepresentation(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { monaco } from "Explorer/LazyMonaco";
|
import { monaco } from "Explorer/LazyMonaco";
|
||||||
|
import { getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
|
||||||
|
|
||||||
export enum QueryErrorSeverity {
|
export enum QueryErrorSeverity {
|
||||||
Error = "Error",
|
Error = "Error",
|
||||||
@@ -102,9 +103,20 @@ export interface ErrorEnrichment {
|
|||||||
learnMoreUrl?: string;
|
learnMoreUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const REPLACEMENT_MESSAGES: Record<string, (original: string) => string> = {};
|
const REPLACEMENT_MESSAGES: Record<string, (original: string) => string> = {
|
||||||
|
OPERATION_RU_LIMIT_EXCEEDED: (original) => {
|
||||||
|
if (ruThresholdEnabled()) {
|
||||||
|
const threshold = getRUThreshold();
|
||||||
|
return `Query exceeded the Request Unit (RU) limit of ${threshold} RUs. You can change this limit in Data Explorer settings.`;
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const HELP_LINKS: Record<string, string> = {};
|
const HELP_LINKS: Record<string, string> = {
|
||||||
|
OPERATION_RU_LIMIT_EXCEEDED:
|
||||||
|
"https://learn.microsoft.com/en-us/azure/cosmos-db/data-explorer#configure-request-unit-threshold",
|
||||||
|
};
|
||||||
|
|
||||||
export default class QueryError {
|
export default class QueryError {
|
||||||
message: string;
|
message: string;
|
||||||
|
|||||||
@@ -4,18 +4,13 @@ import * as React from "react";
|
|||||||
export interface TooltipProps {
|
export interface TooltipProps {
|
||||||
children: string;
|
children: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
ariaLabelForTooltip?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({
|
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children, className }: TooltipProps) => {
|
||||||
children,
|
|
||||||
className,
|
|
||||||
ariaLabelForTooltip = children,
|
|
||||||
}: TooltipProps) => {
|
|
||||||
return (
|
return (
|
||||||
<span className={className}>
|
<span className={className}>
|
||||||
<TooltipHost content={children}>
|
<TooltipHost content={children}>
|
||||||
<Icon iconName="Info" aria-label={ariaLabelForTooltip} className="panelInfoIcon" tabIndex={0} />
|
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
|
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
|
||||||
{
|
{
|
||||||
"enableQueryControl": false,
|
"disableNonStreamingOrderByQuery": true,
|
||||||
"enableScanInQuery": true,
|
"enableScanInQuery": true,
|
||||||
"forceQueryPlan": true,
|
"forceQueryPlan": true,
|
||||||
"maxDegreeOfParallelism": 0,
|
"maxDegreeOfParallelism": 0,
|
||||||
@@ -13,7 +13,7 @@ exports[`getCommonQueryOptions builds the correct default options objects 1`] =
|
|||||||
|
|
||||||
exports[`getCommonQueryOptions reads from localStorage 1`] = `
|
exports[`getCommonQueryOptions reads from localStorage 1`] = `
|
||||||
{
|
{
|
||||||
"enableQueryControl": false,
|
"disableNonStreamingOrderByQuery": true,
|
||||||
"enableScanInQuery": true,
|
"enableScanInQuery": true,
|
||||||
"forceQueryPlan": true,
|
"forceQueryPlan": true,
|
||||||
"maxDegreeOfParallelism": 17,
|
"maxDegreeOfParallelism": 17,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { constructRpOptions } from "Common/dataAccess/createCollection";
|
import { constructRpOptions } from "Common/dataAccess/createCollection";
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
import { Collection, CreateMaterializedViewsParams as CreateGlobalSecondaryIndexParams } from "Contracts/DataModels";
|
import { Collection, CreateMaterializedViewsParams } from "Contracts/DataModels";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { createUpdateSqlContainer } from "Utils/arm/generatedClients/cosmos/sqlResources";
|
import { createUpdateSqlContainer } from "Utils/arm/generatedClients/cosmos/sqlResources";
|
||||||
import {
|
import {
|
||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
} from "Utils/arm/generatedClients/cosmos/types";
|
} from "Utils/arm/generatedClients/cosmos/types";
|
||||||
import { logConsoleInfo, logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
import { logConsoleInfo, logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||||
|
|
||||||
export const createGlobalSecondaryIndex = async (params: CreateGlobalSecondaryIndexParams): Promise<Collection> => {
|
export const createMaterializedView = async (params: CreateMaterializedViewsParams): Promise<Collection> => {
|
||||||
const clearMessage = logConsoleProgress(
|
const clearMessage = logConsoleProgress(
|
||||||
`Creating a new global secondary index ${params.materializedViewId} for database ${params.databaseId}`,
|
`Creating a new materialized view ${params.materializedViewId} for database ${params.databaseId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
const options: CreateUpdateOptions = constructRpOptions(params);
|
||||||
@@ -58,15 +58,11 @@ export const createGlobalSecondaryIndex = async (params: CreateGlobalSecondaryIn
|
|||||||
params.materializedViewId,
|
params.materializedViewId,
|
||||||
rpPayload,
|
rpPayload,
|
||||||
);
|
);
|
||||||
logConsoleInfo(`Successfully created global secondary index ${params.materializedViewId}`);
|
logConsoleInfo(`Successfully created materialized view ${params.materializedViewId}`);
|
||||||
|
|
||||||
return createResponse && (createResponse.properties.resource as Collection);
|
return createResponse && (createResponse.properties.resource as Collection);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(
|
handleError(error, "CreateMaterializedView", `Error while creating materialized view ${params.materializedViewId}`);
|
||||||
error,
|
|
||||||
"CreateGlobalSecondaryIndex",
|
|
||||||
`Error while creating global secondary index ${params.materializedViewId}`,
|
|
||||||
);
|
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ export interface IBulkDeleteResult {
|
|||||||
export const deleteDocuments = async (
|
export const deleteDocuments = async (
|
||||||
collection: CollectionBase,
|
collection: CollectionBase,
|
||||||
documentIds: DocumentId[],
|
documentIds: DocumentId[],
|
||||||
abortSignal: AbortSignal,
|
|
||||||
): Promise<IBulkDeleteResult[]> => {
|
): Promise<IBulkDeleteResult[]> => {
|
||||||
const clearMessage = logConsoleProgress(`Deleting ${documentIds.length} ${getEntityName(true)}`);
|
const clearMessage = logConsoleProgress(`Deleting ${documentIds.length} ${getEntityName(true)}`);
|
||||||
try {
|
try {
|
||||||
@@ -66,11 +65,7 @@ export const deleteDocuments = async (
|
|||||||
operationType: BulkOperationType.Delete,
|
operationType: BulkOperationType.Delete,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const promise = v2Container.items
|
const promise = v2Container.items.bulk(operations).then((bulkResults) => {
|
||||||
.bulk(operations, undefined, {
|
|
||||||
abortSignal,
|
|
||||||
})
|
|
||||||
.then((bulkResults) => {
|
|
||||||
return bulkResults.map((bulkResult, index) => {
|
return bulkResults.map((bulkResult, index) => {
|
||||||
const documentId = documentIdsChunk[index];
|
const documentId = documentIdsChunk[index];
|
||||||
return { ...bulkResult, documentId };
|
return { ...bulkResult, documentId };
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
import { configContext } from "../../ConfigContext";
|
import { configContext } from "../../ConfigContext";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
@@ -42,7 +41,7 @@ interface MetricsResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
|
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
|
||||||
if (userContext.authType !== AuthType.AAD || isFabricNative()) {
|
if (userContext.authType !== AuthType.AAD) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
|
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||||
import { Queries } from "../Constants";
|
import { Queries } from "../Constants";
|
||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
@@ -25,7 +26,7 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
|
|||||||
options.maxItemCount ||
|
options.maxItemCount ||
|
||||||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
|
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
|
||||||
Queries.itemsPerPage;
|
Queries.itemsPerPage;
|
||||||
options.enableQueryControl = LocalStorageUtility.getEntryBoolean(StorageKey.QueryControlEnabled);
|
|
||||||
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
|
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
|
||||||
|
options.disableNonStreamingOrderByQuery = !isVectorSearchEnabled();
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { QueryOperationOptions } from "@azure/cosmos";
|
||||||
import { QueryResults } from "../../Contracts/ViewModels";
|
import { QueryResults } from "../../Contracts/ViewModels";
|
||||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
import { getEntityName } from "../DocumentUtility";
|
import { getEntityName } from "../DocumentUtility";
|
||||||
@@ -8,12 +9,13 @@ export const queryDocumentsPage = async (
|
|||||||
resourceName: string,
|
resourceName: string,
|
||||||
documentsIterator: MinimalQueryIterator,
|
documentsIterator: MinimalQueryIterator,
|
||||||
firstItemIndex: number,
|
firstItemIndex: number,
|
||||||
|
queryOperationOptions?: QueryOperationOptions,
|
||||||
): Promise<QueryResults> => {
|
): Promise<QueryResults> => {
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
|
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
|
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex, queryOperationOptions);
|
||||||
const itemCount = (result.documents && result.documents.length) || 0;
|
const itemCount = (result.documents && result.documents.length) || 0;
|
||||||
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
|
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { isFabric } from "Platform/Fabric/FabricUtil";
|
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
import { Offer, ReadCollectionOfferParams } from "../../Contracts/DataModels";
|
import { Offer, ReadCollectionOfferParams } from "../../Contracts/DataModels";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
@@ -14,11 +13,6 @@ import { readOfferWithSDK } from "./readOfferWithSDK";
|
|||||||
export const readCollectionOffer = async (params: ReadCollectionOfferParams): Promise<Offer> => {
|
export const readCollectionOffer = async (params: ReadCollectionOfferParams): Promise<Offer> => {
|
||||||
const clearMessage = logConsoleProgress(`Querying offer for collection ${params.collectionId}`);
|
const clearMessage = logConsoleProgress(`Querying offer for collection ${params.collectionId}`);
|
||||||
|
|
||||||
if (isFabric()) {
|
|
||||||
// Not exposing offers in Fabric
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
userContext.authType === AuthType.AAD &&
|
userContext.authType === AuthType.AAD &&
|
||||||
|
|||||||
@@ -126,5 +126,12 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
|
|||||||
throw new Error(`Unsupported default experience type: ${apiType}`);
|
throw new Error(`Unsupported default experience type: ${apiType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rpResponse?.value?.map((collection) => collection.properties?.resource as DataModels.Collection);
|
// TO DO: Remove when we get RP API Spec with materializedViews
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
return rpResponse?.value?.map((collection: any) => {
|
||||||
|
const collectionDataModel: DataModels.Collection = collection.properties?.resource as DataModels.Collection;
|
||||||
|
collectionDataModel.materializedViews = collection.properties?.resource?.materializedViews;
|
||||||
|
collectionDataModel.materializedViewDefinition = collection.properties?.resource?.materializedViewDefinition;
|
||||||
|
return collectionDataModel;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { CassandraProxyEndpoints, JunoEndpoints, MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
|
|
||||||
import {
|
import {
|
||||||
|
BackendApi,
|
||||||
|
CassandraProxyEndpoints,
|
||||||
|
JunoEndpoints,
|
||||||
|
MongoProxyEndpoints,
|
||||||
|
PortalBackendEndpoints,
|
||||||
|
} from "Common/Constants";
|
||||||
|
import {
|
||||||
|
allowedAadEndpoints,
|
||||||
allowedArcadiaEndpoints,
|
allowedArcadiaEndpoints,
|
||||||
allowedEmulatorEndpoints,
|
allowedEmulatorEndpoints,
|
||||||
|
allowedGraphEndpoints,
|
||||||
allowedHostedExplorerEndpoints,
|
allowedHostedExplorerEndpoints,
|
||||||
allowedJunoOrigins,
|
allowedJunoOrigins,
|
||||||
allowedMsalRedirectEndpoints,
|
allowedMsalRedirectEndpoints,
|
||||||
defaultAllowedAadEndpoints,
|
|
||||||
defaultAllowedArmEndpoints,
|
defaultAllowedArmEndpoints,
|
||||||
defaultAllowedBackendEndpoints,
|
defaultAllowedBackendEndpoints,
|
||||||
defaultAllowedCassandraProxyEndpoints,
|
defaultAllowedCassandraProxyEndpoints,
|
||||||
defaultAllowedGraphEndpoints,
|
|
||||||
defaultAllowedMongoProxyEndpoints,
|
defaultAllowedMongoProxyEndpoints,
|
||||||
validateEndpoint,
|
validateEndpoint,
|
||||||
} from "Utils/EndpointUtils";
|
} from "Utils/EndpointUtils";
|
||||||
@@ -23,8 +29,6 @@ export enum Platform {
|
|||||||
|
|
||||||
export interface ConfigContext {
|
export interface ConfigContext {
|
||||||
platform: Platform;
|
platform: Platform;
|
||||||
allowedAadEndpoints: ReadonlyArray<string>;
|
|
||||||
allowedGraphEndpoints: ReadonlyArray<string>;
|
|
||||||
allowedArmEndpoints: ReadonlyArray<string>;
|
allowedArmEndpoints: ReadonlyArray<string>;
|
||||||
allowedBackendEndpoints: ReadonlyArray<string>;
|
allowedBackendEndpoints: ReadonlyArray<string>;
|
||||||
allowedCassandraProxyEndpoints: ReadonlyArray<string>;
|
allowedCassandraProxyEndpoints: ReadonlyArray<string>;
|
||||||
@@ -33,8 +37,10 @@ export interface ConfigContext {
|
|||||||
gitSha?: string;
|
gitSha?: string;
|
||||||
proxyPath?: string;
|
proxyPath?: string;
|
||||||
AAD_ENDPOINT: string;
|
AAD_ENDPOINT: string;
|
||||||
|
ARM_AUTH_AREA: string;
|
||||||
ARM_ENDPOINT: string;
|
ARM_ENDPOINT: string;
|
||||||
EMULATOR_ENDPOINT?: string;
|
EMULATOR_ENDPOINT?: string;
|
||||||
|
ARM_API_VERSION: string;
|
||||||
GRAPH_ENDPOINT: string;
|
GRAPH_ENDPOINT: string;
|
||||||
GRAPH_API_VERSION: string;
|
GRAPH_API_VERSION: string;
|
||||||
// This is the endpoint to get offering Ids to be used to fetch prices. Refer to this doc: https://learn.microsoft.com/en-us/rest/api/marketplacecatalog/dataplane/skus/list?view=rest-marketplacecatalog-dataplane-2023-05-01-preview&tabs=HTTP
|
// This is the endpoint to get offering Ids to be used to fetch prices. Refer to this doc: https://learn.microsoft.com/en-us/rest/api/marketplacecatalog/dataplane/skus/list?view=rest-marketplacecatalog-dataplane-2023-05-01-preview&tabs=HTTP
|
||||||
@@ -44,24 +50,27 @@ export interface ConfigContext {
|
|||||||
ARCADIA_ENDPOINT: string;
|
ARCADIA_ENDPOINT: string;
|
||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
||||||
PORTAL_BACKEND_ENDPOINT: string;
|
PORTAL_BACKEND_ENDPOINT: string;
|
||||||
|
NEW_BACKEND_APIS?: BackendApi[];
|
||||||
MONGO_PROXY_ENDPOINT: string;
|
MONGO_PROXY_ENDPOINT: string;
|
||||||
CASSANDRA_PROXY_ENDPOINT: string;
|
CASSANDRA_PROXY_ENDPOINT: string;
|
||||||
|
NEW_CASSANDRA_APIS?: string[];
|
||||||
PROXY_PATH?: string;
|
PROXY_PATH?: string;
|
||||||
JUNO_ENDPOINT: string;
|
JUNO_ENDPOINT: string;
|
||||||
GITHUB_CLIENT_ID: string;
|
GITHUB_CLIENT_ID: string;
|
||||||
GITHUB_TEST_ENV_CLIENT_ID: string;
|
GITHUB_TEST_ENV_CLIENT_ID: string;
|
||||||
GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it.
|
GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it.
|
||||||
|
isTerminalEnabled: boolean;
|
||||||
isPhoenixEnabled: boolean;
|
isPhoenixEnabled: boolean;
|
||||||
hostedExplorerURL: string;
|
hostedExplorerURL: string;
|
||||||
armAPIVersion?: string;
|
armAPIVersion?: string;
|
||||||
msalRedirectURI?: string;
|
msalRedirectURI?: string;
|
||||||
|
globallyEnabledCassandraAPIs?: string[];
|
||||||
|
globallyEnabledMongoAPIs?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default configuration
|
// Default configuration
|
||||||
let configContext: Readonly<ConfigContext> = {
|
let configContext: Readonly<ConfigContext> = {
|
||||||
platform: Platform.Portal,
|
platform: Platform.Portal,
|
||||||
allowedAadEndpoints: defaultAllowedAadEndpoints,
|
|
||||||
allowedGraphEndpoints: defaultAllowedGraphEndpoints,
|
|
||||||
allowedArmEndpoints: defaultAllowedArmEndpoints,
|
allowedArmEndpoints: defaultAllowedArmEndpoints,
|
||||||
allowedBackendEndpoints: defaultAllowedBackendEndpoints,
|
allowedBackendEndpoints: defaultAllowedBackendEndpoints,
|
||||||
allowedCassandraProxyEndpoints: defaultAllowedCassandraProxyEndpoints,
|
allowedCassandraProxyEndpoints: defaultAllowedCassandraProxyEndpoints,
|
||||||
@@ -76,12 +85,17 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
`^https:\\/\\/cosmos-db-dataexplorer-germanycentral\\.azurewebsites\\.de$`,
|
`^https:\\/\\/cosmos-db-dataexplorer-germanycentral\\.azurewebsites\\.de$`,
|
||||||
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
|
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
|
||||||
`^https:\\/\\/.*\\.powerbi\\.com$`,
|
`^https:\\/\\/.*\\.powerbi\\.com$`,
|
||||||
|
`^https:\\/\\/.*\\.analysis-df\\.net$`,
|
||||||
|
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
|
||||||
|
`^https:\\/\\/.*\\.azure-test\\.net$`,
|
||||||
`^https:\\/\\/dataexplorer-preview\\.azurewebsites\\.net$`,
|
`^https:\\/\\/dataexplorer-preview\\.azurewebsites\\.net$`,
|
||||||
], // Webpack injects this at build time
|
], // Webpack injects this at build time
|
||||||
gitSha: process.env.GIT_SHA,
|
gitSha: process.env.GIT_SHA,
|
||||||
hostedExplorerURL: "https://cosmos.azure.com/",
|
hostedExplorerURL: "https://cosmos.azure.com/",
|
||||||
AAD_ENDPOINT: "https://login.microsoftonline.com/",
|
AAD_ENDPOINT: "https://login.microsoftonline.com/",
|
||||||
|
ARM_AUTH_AREA: "https://management.azure.com/",
|
||||||
ARM_ENDPOINT: "https://management.azure.com/",
|
ARM_ENDPOINT: "https://management.azure.com/",
|
||||||
|
ARM_API_VERSION: "2016-06-01",
|
||||||
GRAPH_ENDPOINT: "https://graph.microsoft.com",
|
GRAPH_ENDPOINT: "https://graph.microsoft.com",
|
||||||
GRAPH_API_VERSION: "1.6",
|
GRAPH_API_VERSION: "1.6",
|
||||||
CATALOG_ENDPOINT: "https://catalogapi.azure.com/",
|
CATALOG_ENDPOINT: "https://catalogapi.azure.com/",
|
||||||
@@ -95,7 +109,11 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
|
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
|
||||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||||
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Prod,
|
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Prod,
|
||||||
|
NEW_CASSANDRA_APIS: ["postQuery", "createOrDelete", "getKeys", "getSchema"],
|
||||||
|
isTerminalEnabled: false,
|
||||||
isPhoenixEnabled: false,
|
isPhoenixEnabled: false,
|
||||||
|
globallyEnabledCassandraAPIs: [],
|
||||||
|
globallyEnabledMongoAPIs: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resetConfigContext(): void {
|
export function resetConfigContext(): void {
|
||||||
@@ -110,21 +128,19 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateEndpoint(newContext.AAD_ENDPOINT, configContext.allowedAadEndpoints || defaultAllowedAadEndpoints)) {
|
|
||||||
delete newContext.AAD_ENDPOINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateEndpoint(newContext.ARM_ENDPOINT, configContext.allowedArmEndpoints || defaultAllowedArmEndpoints)) {
|
if (!validateEndpoint(newContext.ARM_ENDPOINT, configContext.allowedArmEndpoints || defaultAllowedArmEndpoints)) {
|
||||||
delete newContext.ARM_ENDPOINT;
|
delete newContext.ARM_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!validateEndpoint(newContext.AAD_ENDPOINT, allowedAadEndpoints)) {
|
||||||
|
delete newContext.AAD_ENDPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
if (!validateEndpoint(newContext.EMULATOR_ENDPOINT, allowedEmulatorEndpoints)) {
|
if (!validateEndpoint(newContext.EMULATOR_ENDPOINT, allowedEmulatorEndpoints)) {
|
||||||
delete newContext.EMULATOR_ENDPOINT;
|
delete newContext.EMULATOR_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!validateEndpoint(newContext.GRAPH_ENDPOINT, allowedGraphEndpoints)) {
|
||||||
!validateEndpoint(newContext.GRAPH_ENDPOINT, configContext.allowedGraphEndpoints || defaultAllowedGraphEndpoints)
|
|
||||||
) {
|
|
||||||
delete newContext.GRAPH_ENDPOINT;
|
delete newContext.GRAPH_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,15 +148,6 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
|||||||
delete newContext.ARCADIA_ENDPOINT;
|
delete newContext.ARCADIA_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
!validateEndpoint(
|
|
||||||
newContext.PORTAL_BACKEND_ENDPOINT,
|
|
||||||
configContext.allowedBackendEndpoints || defaultAllowedBackendEndpoints,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
delete newContext.PORTAL_BACKEND_ENDPOINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!validateEndpoint(
|
!validateEndpoint(
|
||||||
newContext.MONGO_PROXY_ENDPOINT,
|
newContext.MONGO_PROXY_ENDPOINT,
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export enum PaneKind {
|
|||||||
GlobalSettings,
|
GlobalSettings,
|
||||||
AdHocAccess,
|
AdHocAccess,
|
||||||
SwitchDirectory,
|
SwitchDirectory,
|
||||||
QuickStart,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,13 +18,10 @@ export type DataExploreMessageV3 =
|
|||||||
| {
|
| {
|
||||||
type: FabricMessageTypes.GetAllResourceTokens;
|
type: FabricMessageTypes.GetAllResourceTokens;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: FabricMessageTypes.OpenSettings;
|
|
||||||
settingsId: string;
|
|
||||||
};
|
};
|
||||||
export interface GetCosmosTokenMessageOptions {
|
|
||||||
|
export type GetCosmosTokenMessageOptions = {
|
||||||
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";
|
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";
|
||||||
resourceType: "" | "dbs" | "colls" | "docs" | "sprocs" | "pkranges";
|
resourceType: "" | "dbs" | "colls" | "docs" | "sprocs" | "pkranges";
|
||||||
resourceId: string;
|
resourceId: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export interface ArmEntity {
|
|||||||
type: string;
|
type: string;
|
||||||
kind: string;
|
kind: string;
|
||||||
tags?: Tags;
|
tags?: Tags;
|
||||||
resourceGroup?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseAccount extends ArmEntity {
|
export interface DatabaseAccount extends ArmEntity {
|
||||||
@@ -211,7 +210,7 @@ export interface IndexingPolicy {
|
|||||||
export interface VectorIndex {
|
export interface VectorIndex {
|
||||||
path: string;
|
path: string;
|
||||||
type: "flat" | "diskANN" | "quantizedFlat";
|
type: "flat" | "diskANN" | "quantizedFlat";
|
||||||
vectorIndexShardKey?: string[];
|
diskANNShardKey?: string;
|
||||||
indexingSearchListSize?: number;
|
indexingSearchListSize?: number;
|
||||||
quantizationByteSize?: number;
|
quantizationByteSize?: number;
|
||||||
}
|
}
|
||||||
@@ -389,7 +388,7 @@ export interface VectorEmbeddingPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface VectorEmbedding {
|
export interface VectorEmbedding {
|
||||||
dataType: "float32" | "uint8" | "int8";
|
dataType: "float16" | "float32" | "uint8" | "int8";
|
||||||
dimensions: number;
|
dimensions: number;
|
||||||
distanceFunction: "euclidean" | "cosine" | "dotproduct";
|
distanceFunction: "euclidean" | "cosine" | "dotproduct";
|
||||||
path: string;
|
path: string;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ export enum FabricMessageTypes {
|
|||||||
GetAllResourceTokens = "GetAllResourceTokens",
|
GetAllResourceTokens = "GetAllResourceTokens",
|
||||||
GetAccessToken = "GetAccessToken",
|
GetAccessToken = "GetAccessToken",
|
||||||
Ready = "Ready",
|
Ready = "Ready",
|
||||||
OpenSettings = "OpenSettings",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthorizationToken {
|
export interface AuthorizationToken {
|
||||||
|
|||||||
@@ -81,13 +81,6 @@ export type FabricMessageV3 =
|
|||||||
error: string | undefined;
|
error: string | undefined;
|
||||||
data: { accessToken: string };
|
data: { accessToken: string };
|
||||||
};
|
};
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "refreshResourceTree";
|
|
||||||
message: {
|
|
||||||
id: string;
|
|
||||||
error: string | undefined;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum CosmosDbArtifactType {
|
export enum CosmosDbArtifactType {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
ItemDefinition,
|
|
||||||
JSONObject,
|
|
||||||
QueryMetrics,
|
QueryMetrics,
|
||||||
Resource,
|
Resource,
|
||||||
StoredProcedureDefinition,
|
StoredProcedureDefinition,
|
||||||
@@ -31,11 +29,8 @@ export interface UploadDetailsRecord {
|
|||||||
numFailed: number;
|
numFailed: number;
|
||||||
numThrottled: number;
|
numThrottled: number;
|
||||||
errors: string[];
|
errors: string[];
|
||||||
resources?: ItemDefinition[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BulkInsertResult = Omit<UploadDetailsRecord, "fileName">;
|
|
||||||
|
|
||||||
export interface QueryResultsMetadata {
|
export interface QueryResultsMetadata {
|
||||||
hasMoreResults: boolean;
|
hasMoreResults: boolean;
|
||||||
firstItemIndex: number;
|
firstItemIndex: number;
|
||||||
@@ -50,7 +45,6 @@ export interface QueryResults extends QueryResultsMetadata {
|
|||||||
roundTrips?: number;
|
roundTrips?: number;
|
||||||
headers?: any;
|
headers?: any;
|
||||||
queryMetrics?: QueryMetrics;
|
queryMetrics?: QueryMetrics;
|
||||||
ruThresholdExceeded?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Button {
|
export interface Button {
|
||||||
@@ -212,12 +206,6 @@ export interface Collection extends CollectionBase {
|
|||||||
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
|
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||||
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||||
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
||||||
bulkInsertDocuments(documents: JSONObject[]): Promise<{
|
|
||||||
numSucceeded: number;
|
|
||||||
numFailed: number;
|
|
||||||
numThrottled: number;
|
|
||||||
errors: string[];
|
|
||||||
}>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -443,7 +431,6 @@ export interface DataExplorerInputsFrame {
|
|||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
feedbackPolicies?: any;
|
feedbackPolicies?: any;
|
||||||
aadToken?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelfServeFrameInputs {
|
export interface SelfServeFrameInputs {
|
||||||
|
|||||||
11
src/Controls/Heatmap/Heatmap.html
Normal file
11
src/Controls/Heatmap/Heatmap.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="no-js" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="data:," />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="heatmap"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
55
src/Controls/Heatmap/Heatmap.less
Normal file
55
src/Controls/Heatmap/Heatmap.less
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
@import "../../../less/Common/Constants";
|
||||||
|
html {
|
||||||
|
font-family: @DataExplorerFont;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: @DataExplorerFont;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
border: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#heatmap {
|
||||||
|
.dark-theme {
|
||||||
|
color: @BaseLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartTitle {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noDataMessage {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10000;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.97;
|
||||||
|
div {
|
||||||
|
border-color: rgba(204, 204, 204, 0.8);
|
||||||
|
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
padding: 15px 10px;
|
||||||
|
width: calc(55% - 40px);
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
143
src/Controls/Heatmap/Heatmap.test.ts
Normal file
143
src/Controls/Heatmap/Heatmap.test.ts
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
import { handleMessage, Heatmap, isDarkTheme } from "./Heatmap";
|
||||||
|
import { PortalTheme } from "./HeatmapDatatypes";
|
||||||
|
|
||||||
|
describe("The Heatmap Control", () => {
|
||||||
|
const dataPoints = {
|
||||||
|
"1": {
|
||||||
|
"2019-06-19T00:59:10Z": {
|
||||||
|
"Normalized Throughput": 0.35,
|
||||||
|
},
|
||||||
|
"2019-06-19T00:48:10Z": {
|
||||||
|
"Normalized Throughput": 0.25,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartCaptions = {
|
||||||
|
chartTitle: "chart title",
|
||||||
|
yAxisTitle: "YAxisTitle",
|
||||||
|
tooltipText: "Tooltip text",
|
||||||
|
timeWindow: 123456789,
|
||||||
|
};
|
||||||
|
|
||||||
|
let heatmap: Heatmap;
|
||||||
|
const theme: PortalTheme = 1;
|
||||||
|
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
|
|
||||||
|
describe("drawHeatmap rendering", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
heatmap = new Heatmap(dataPoints, chartCaptions, theme);
|
||||||
|
document.body.innerHTML = divElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.innerHTML = ``;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call _getChartSettings when drawHeatmap is invoked", () => {
|
||||||
|
const _getChartSettings = jest.spyOn(heatmap, "_getChartSettings");
|
||||||
|
heatmap.drawHeatmap();
|
||||||
|
expect(_getChartSettings).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call _getLayoutSettings when drawHeatmap is invoked", () => {
|
||||||
|
const _getLayoutSettings = jest.spyOn(heatmap, "_getLayoutSettings");
|
||||||
|
heatmap.drawHeatmap();
|
||||||
|
expect(_getLayoutSettings).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call _getChartDisplaySettings when drawHeatmap is invoked", () => {
|
||||||
|
const _getChartDisplaySettings = jest.spyOn(heatmap, "_getChartDisplaySettings");
|
||||||
|
heatmap.drawHeatmap();
|
||||||
|
expect(_getChartDisplaySettings).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("drawHeatmap should render a Heatmap inside the div element", () => {
|
||||||
|
heatmap.drawHeatmap();
|
||||||
|
expect(document.body.innerHTML).not.toEqual(divElement);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("generateMatrixFromMap", () => {
|
||||||
|
it("should massage input data to match output expected", () => {
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).yAxisPoints).toEqual(["1"]);
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).dataPoints).toEqual([[0.25, 0.35]]);
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints.length).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should output the date format to ISO8601 string format", () => {
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints[0].slice(10, 11)).toEqual("T");
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints[0].slice(-1)).toEqual("Z");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert the time to the user's local time", () => {
|
||||||
|
if (dayjs().utcOffset()) {
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
||||||
|
"2019-06-19T00:48:10Z",
|
||||||
|
"2019-06-19T00:59:10Z",
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
||||||
|
"2019-06-19T00:48:10Z",
|
||||||
|
"2019-06-19T00:59:10Z",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isDarkTheme", () => {
|
||||||
|
it("isDarkTheme should return the correct result", () => {
|
||||||
|
expect(isDarkTheme(PortalTheme.dark)).toEqual(true);
|
||||||
|
expect(isDarkTheme(PortalTheme.azure)).not.toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("iframe rendering when there is no data", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.innerHTML = ``;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show a no data message with a dark theme", () => {
|
||||||
|
const data = {
|
||||||
|
data: {
|
||||||
|
signature: "pcIframe",
|
||||||
|
data: {
|
||||||
|
chartData: {},
|
||||||
|
chartSettings: {},
|
||||||
|
theme: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
origin: "http://localhost",
|
||||||
|
};
|
||||||
|
|
||||||
|
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
|
document.body.innerHTML = divElement;
|
||||||
|
|
||||||
|
handleMessage(data as MessageEvent);
|
||||||
|
expect(document.body.innerHTML).toContain("dark-theme");
|
||||||
|
expect(document.body.innerHTML).toContain("noDataMessage");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show a no data message with a white theme", () => {
|
||||||
|
const data = {
|
||||||
|
data: {
|
||||||
|
signature: "pcIframe",
|
||||||
|
data: {
|
||||||
|
chartData: {},
|
||||||
|
chartSettings: {},
|
||||||
|
theme: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
origin: "http://localhost",
|
||||||
|
};
|
||||||
|
|
||||||
|
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
|
document.body.innerHTML = divElement;
|
||||||
|
|
||||||
|
handleMessage(data as MessageEvent);
|
||||||
|
expect(document.body.innerHTML).not.toContain("dark-theme");
|
||||||
|
expect(document.body.innerHTML).toContain("noDataMessage");
|
||||||
|
});
|
||||||
|
});
|
||||||
272
src/Controls/Heatmap/Heatmap.ts
Normal file
272
src/Controls/Heatmap/Heatmap.ts
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
import * as Plotly from "plotly.js-cartesian-dist-min";
|
||||||
|
import { sendCachedDataMessage, sendReadyMessage } from "../../Common/MessageHandler";
|
||||||
|
import { StyleConstants } from "../../Common/StyleConstants";
|
||||||
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||||
|
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||||
|
import "./Heatmap.less";
|
||||||
|
import {
|
||||||
|
ChartSettings,
|
||||||
|
DataPayload,
|
||||||
|
DisplaySettings,
|
||||||
|
FontSettings,
|
||||||
|
HeatmapCaptions,
|
||||||
|
HeatmapData,
|
||||||
|
LayoutSettings,
|
||||||
|
PartitionTimeStampToData,
|
||||||
|
PortalTheme,
|
||||||
|
} from "./HeatmapDatatypes";
|
||||||
|
|
||||||
|
export class Heatmap {
|
||||||
|
public static readonly elementId: string = "heatmap";
|
||||||
|
|
||||||
|
private _chartData: HeatmapData;
|
||||||
|
private _heatmapCaptions: HeatmapCaptions;
|
||||||
|
private _theme: PortalTheme;
|
||||||
|
private _defaultFontColor: string;
|
||||||
|
|
||||||
|
constructor(data: DataPayload, heatmapCaptions: HeatmapCaptions, theme: PortalTheme) {
|
||||||
|
this._theme = theme;
|
||||||
|
this._defaultFontColor = StyleConstants.BaseDark;
|
||||||
|
this._setThemeColorForChart();
|
||||||
|
this._chartData = this.generateMatrixFromMap(data);
|
||||||
|
this._heatmapCaptions = heatmapCaptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setThemeColorForChart() {
|
||||||
|
if (isDarkTheme(this._theme)) {
|
||||||
|
this._defaultFontColor = StyleConstants.BaseLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getFontStyles(size: number = StyleConstants.MediumFontSize, color = "#838383"): FontSettings {
|
||||||
|
return {
|
||||||
|
family: StyleConstants.DataExplorerFont,
|
||||||
|
size,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateMatrixFromMap(data: DataPayload): HeatmapData {
|
||||||
|
// all keys in data payload, sorted...
|
||||||
|
const rows: string[] = Object.keys(data).sort((a: string, b: string) => {
|
||||||
|
if (parseInt(a) < parseInt(b)) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (parseInt(a) > parseInt(b)) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const output: HeatmapData = {
|
||||||
|
yAxisPoints: [],
|
||||||
|
dataPoints: [],
|
||||||
|
xAxisPoints: Object.keys(data[rows[0]]).sort((a: string, b: string) => {
|
||||||
|
if (a < b) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (a > b) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
// go thru all rows and create 2d matrix for heatmap...
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
output.yAxisPoints.push(rows[i]);
|
||||||
|
const dataPoints: number[] = [];
|
||||||
|
for (let a = 0; a < output.xAxisPoints.length; a++) {
|
||||||
|
const row: PartitionTimeStampToData = data[rows[i]];
|
||||||
|
dataPoints.push(row[output.xAxisPoints[a]]["Normalized Throughput"]);
|
||||||
|
}
|
||||||
|
output.dataPoints.push(dataPoints);
|
||||||
|
}
|
||||||
|
for (let a = 0; a < output.xAxisPoints.length; a++) {
|
||||||
|
const dateTime = output.xAxisPoints[a];
|
||||||
|
// convert to local users timezone...
|
||||||
|
const day = dayjs(new Date(dateTime)).format("YYYY-MM-DD");
|
||||||
|
const hour = dayjs(new Date(dateTime)).format("HH:mm:ss");
|
||||||
|
// coerce to ISOString format since that is what plotly wants...
|
||||||
|
output.xAxisPoints[a] = `${day}T${hour}Z`;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public for testing purposes
|
||||||
|
public _getChartSettings(): ChartSettings[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
z: this._chartData.dataPoints,
|
||||||
|
type: "heatmap",
|
||||||
|
zmin: 0,
|
||||||
|
zmid: 50,
|
||||||
|
zmax: 100,
|
||||||
|
colorscale: [
|
||||||
|
[0.0, "#1FD338"],
|
||||||
|
[0.1, "#1CAD2F"],
|
||||||
|
[0.2, "#50A527"],
|
||||||
|
[0.3, "#719F21"],
|
||||||
|
[0.4, "#95991B"],
|
||||||
|
[0.5, "#CE8F11"],
|
||||||
|
[0.6, "#E27F0F"],
|
||||||
|
[0.7, "#E46612"],
|
||||||
|
[0.8, "#E64914"],
|
||||||
|
[0.9, "#B80016"],
|
||||||
|
[1.0, "#B80016"],
|
||||||
|
],
|
||||||
|
name: "",
|
||||||
|
hovertemplate: this._heatmapCaptions.tooltipText,
|
||||||
|
colorbar: {
|
||||||
|
thickness: 15,
|
||||||
|
outlinewidth: 0,
|
||||||
|
tickcolor: StyleConstants.BaseDark,
|
||||||
|
tickfont: this._getFontStyles(10, this._defaultFontColor),
|
||||||
|
},
|
||||||
|
y: this._chartData.yAxisPoints,
|
||||||
|
x: this._chartData.xAxisPoints,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// public for testing purposes
|
||||||
|
public _getLayoutSettings(): LayoutSettings {
|
||||||
|
return {
|
||||||
|
margin: {
|
||||||
|
l: 40,
|
||||||
|
r: 10,
|
||||||
|
b: 35,
|
||||||
|
t: 30,
|
||||||
|
pad: 0,
|
||||||
|
},
|
||||||
|
paper_bgcolor: "transparent",
|
||||||
|
plot_bgcolor: "transparent",
|
||||||
|
width: 462,
|
||||||
|
height: 240,
|
||||||
|
yaxis: {
|
||||||
|
title: this._heatmapCaptions.yAxisTitle,
|
||||||
|
titlefont: this._getFontStyles(11),
|
||||||
|
autorange: true,
|
||||||
|
showgrid: false,
|
||||||
|
zeroline: false,
|
||||||
|
showline: false,
|
||||||
|
autotick: true,
|
||||||
|
fixedrange: true,
|
||||||
|
ticks: "",
|
||||||
|
showticklabels: false,
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
fixedrange: true,
|
||||||
|
title: "*White area in heatmap indicates there is no available data",
|
||||||
|
titlefont: this._getFontStyles(11),
|
||||||
|
autorange: true,
|
||||||
|
showgrid: false,
|
||||||
|
zeroline: false,
|
||||||
|
showline: false,
|
||||||
|
autotick: true,
|
||||||
|
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
||||||
|
showticklabels: true,
|
||||||
|
tickfont: this._getFontStyles(10),
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: this._heatmapCaptions.chartTitle,
|
||||||
|
x: 0.01,
|
||||||
|
font: this._getFontStyles(13, this._defaultFontColor),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// public for testing purposes
|
||||||
|
public _getChartDisplaySettings(): DisplaySettings {
|
||||||
|
return {
|
||||||
|
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
||||||
|
responsive: true,*/
|
||||||
|
displayModeBar: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawHeatmap(): void {
|
||||||
|
// todo - create random elementId generator so multiple heatmaps can be created - ticket # 431469
|
||||||
|
Plotly.plot(
|
||||||
|
Heatmap.elementId,
|
||||||
|
this._getChartSettings(),
|
||||||
|
this._getLayoutSettings(),
|
||||||
|
this._getChartDisplaySettings(),
|
||||||
|
);
|
||||||
|
const plotDiv: any = document.getElementById(Heatmap.elementId);
|
||||||
|
plotDiv.on("plotly_click", (data: any) => {
|
||||||
|
let timeSelected: string = data.points[0].x;
|
||||||
|
timeSelected = timeSelected.replace(" ", "T");
|
||||||
|
timeSelected = `${timeSelected}Z`;
|
||||||
|
let xAxisIndex = 0;
|
||||||
|
for (let i = 0; i < this._chartData.xAxisPoints.length; i++) {
|
||||||
|
if (this._chartData.xAxisPoints[i] === timeSelected) {
|
||||||
|
xAxisIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const output = [];
|
||||||
|
for (let i = 0; i < this._chartData.dataPoints.length; i++) {
|
||||||
|
output.push(this._chartData.dataPoints[i][xAxisIndex]);
|
||||||
|
}
|
||||||
|
sendCachedDataMessage(MessageTypes.LogInfo, output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDarkTheme(theme: PortalTheme) {
|
||||||
|
return theme === PortalTheme.dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleMessage(event: MessageEvent) {
|
||||||
|
if (isInvalidParentFrameOrigin(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof event.data !== "object" || event.data["signature"] !== "pcIframe") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof event.data.data !== "object" ||
|
||||||
|
!("chartData" in event.data.data) ||
|
||||||
|
!("chartSettings" in event.data.data)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Plotly.purge(Heatmap.elementId);
|
||||||
|
|
||||||
|
document.getElementById(Heatmap.elementId)!.innerHTML = "";
|
||||||
|
const data = event.data.data;
|
||||||
|
const chartData: DataPayload = data.chartData;
|
||||||
|
const chartSettings: HeatmapCaptions = data.chartSettings;
|
||||||
|
const chartTheme: PortalTheme = data.theme;
|
||||||
|
if (Object.keys(chartData).length) {
|
||||||
|
new Heatmap(chartData, chartSettings, chartTheme).drawHeatmap();
|
||||||
|
} else {
|
||||||
|
const chartTitleElement = document.createElement("div");
|
||||||
|
chartTitleElement.innerHTML = data.chartSettings.chartTitle;
|
||||||
|
chartTitleElement.classList.add("chartTitle");
|
||||||
|
|
||||||
|
const noDataMessageElement = document.createElement("div");
|
||||||
|
noDataMessageElement.classList.add("noDataMessage");
|
||||||
|
const noDataMessageContent = document.createElement("div");
|
||||||
|
noDataMessageContent.innerHTML = data.errorMessage;
|
||||||
|
|
||||||
|
noDataMessageElement.appendChild(noDataMessageContent);
|
||||||
|
|
||||||
|
if (isDarkTheme(chartTheme)) {
|
||||||
|
chartTitleElement.classList.add("dark-theme");
|
||||||
|
noDataMessageElement.classList.add("dark-theme");
|
||||||
|
noDataMessageContent.classList.add("dark-theme");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById(Heatmap.elementId)!.appendChild(chartTitleElement);
|
||||||
|
document.getElementById(Heatmap.elementId)!.appendChild(noDataMessageElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("message", handleMessage, false);
|
||||||
|
sendReadyMessage();
|
||||||
106
src/Controls/Heatmap/HeatmapDatatypes.ts
Normal file
106
src/Controls/Heatmap/HeatmapDatatypes.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
type dataPoint = string | number;
|
||||||
|
|
||||||
|
export interface DataPayload {
|
||||||
|
[id: string]: PartitionTimeStampToData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PortalTheme {
|
||||||
|
blue = 1,
|
||||||
|
azure,
|
||||||
|
light,
|
||||||
|
dark,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HeatmapData {
|
||||||
|
yAxisPoints: string[];
|
||||||
|
xAxisPoints: string[];
|
||||||
|
dataPoints: dataPoint[][];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HeatmapCaptions {
|
||||||
|
chartTitle: string;
|
||||||
|
yAxisTitle: string;
|
||||||
|
tooltipText: string;
|
||||||
|
timeWindow: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FontSettings {
|
||||||
|
family: string;
|
||||||
|
size: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayoutSettings {
|
||||||
|
paper_bgcolor?: string;
|
||||||
|
plot_bgcolor?: string;
|
||||||
|
margin?: {
|
||||||
|
l: number;
|
||||||
|
r: number;
|
||||||
|
b: number;
|
||||||
|
t: number;
|
||||||
|
pad: number;
|
||||||
|
};
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
yaxis?: {
|
||||||
|
fixedrange: boolean;
|
||||||
|
title: HeatmapCaptions["yAxisTitle"];
|
||||||
|
titlefont: FontSettings;
|
||||||
|
autorange: boolean;
|
||||||
|
showgrid: boolean;
|
||||||
|
zeroline: boolean;
|
||||||
|
showline: boolean;
|
||||||
|
autotick: boolean;
|
||||||
|
ticks: "";
|
||||||
|
showticklabels: boolean;
|
||||||
|
};
|
||||||
|
xaxis?: {
|
||||||
|
fixedrange: boolean;
|
||||||
|
title: string;
|
||||||
|
titlefont: FontSettings;
|
||||||
|
autorange: boolean;
|
||||||
|
showgrid: boolean;
|
||||||
|
zeroline: boolean;
|
||||||
|
showline: boolean;
|
||||||
|
autotick: boolean;
|
||||||
|
showticklabels: boolean;
|
||||||
|
tickformat: string;
|
||||||
|
tickfont: FontSettings;
|
||||||
|
};
|
||||||
|
title?: {
|
||||||
|
text: HeatmapCaptions["chartTitle"];
|
||||||
|
x: number;
|
||||||
|
font?: FontSettings;
|
||||||
|
};
|
||||||
|
font?: FontSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChartSettings {
|
||||||
|
z: HeatmapData["dataPoints"];
|
||||||
|
type: "heatmap";
|
||||||
|
zmin: number;
|
||||||
|
zmid: number;
|
||||||
|
zmax: number;
|
||||||
|
colorscale: [number, string][];
|
||||||
|
name: string;
|
||||||
|
hovertemplate: HeatmapCaptions["tooltipText"];
|
||||||
|
colorbar: {
|
||||||
|
thickness: number;
|
||||||
|
outlinewidth: number;
|
||||||
|
tickcolor: string;
|
||||||
|
tickfont: FontSettings;
|
||||||
|
};
|
||||||
|
y: HeatmapData["yAxisPoints"];
|
||||||
|
x: HeatmapData["xAxisPoints"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DisplaySettings {
|
||||||
|
displayModeBar: boolean;
|
||||||
|
responsive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PartitionTimeStampToData {
|
||||||
|
[timeSeriesDates: string]: {
|
||||||
|
[NormalizedThroughput: string]: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { GlobalSecondaryIndexLabels } from "Common/Constants";
|
import { MaterializedViewsLabels } from "Common/Constants";
|
||||||
import { isGlobalSecondaryIndexEnabled } from "Common/DatabaseAccountUtility";
|
import { isMaterializedViewsEnabled } from "Common/DatabaseAccountUtility";
|
||||||
import { configContext, Platform } from "ConfigContext";
|
import { configContext, Platform } from "ConfigContext";
|
||||||
import { TreeNodeMenuItem } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
|
import { TreeNodeMenuItem } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
|
||||||
import {
|
import {
|
||||||
AddGlobalSecondaryIndexPanel,
|
AddMaterializedViewPanel,
|
||||||
AddGlobalSecondaryIndexPanelProps,
|
AddMaterializedViewPanelProps,
|
||||||
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
} from "Explorer/Panes/AddMaterializedViewPanel/AddMaterializedViewPanel";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
|
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
@@ -103,23 +103,17 @@ export const createCollectionContextMenuButton = (
|
|||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||||
if (useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell) {
|
if (useNotebook.getState().isShellEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
} else {
|
} else {
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label:
|
label: useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell",
|
||||||
useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell
|
|
||||||
? "Open Mongo Shell"
|
|
||||||
: "New Shell",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (useNotebook.getState().isShellEnabled && userContext.apiType === "Cassandra") {
|
||||||
(useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell) &&
|
|
||||||
userContext.apiType === "Cassandra"
|
|
||||||
) {
|
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -176,19 +170,19 @@ export const createCollectionContextMenuButton = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isGlobalSecondaryIndexEnabled() && !selectedCollection.materializedViewDefinition()) {
|
if (isMaterializedViewsEnabled() && !selectedCollection.materializedViewDefinition()) {
|
||||||
items.push({
|
items.push({
|
||||||
label: GlobalSecondaryIndexLabels.NewGlobalSecondaryIndex,
|
label: MaterializedViewsLabels.NewMaterializedView,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const addMaterializedViewPanelProps: AddGlobalSecondaryIndexPanelProps = {
|
const addMaterializedViewPanelProps: AddMaterializedViewPanelProps = {
|
||||||
explorer: container,
|
explorer: container,
|
||||||
sourceContainer: selectedCollection,
|
sourceContainer: selectedCollection,
|
||||||
};
|
};
|
||||||
useSidePanel
|
useSidePanel
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel(
|
.openSidePanel(
|
||||||
GlobalSecondaryIndexLabels.NewGlobalSecondaryIndex,
|
MaterializedViewsLabels.NewMaterializedView,
|
||||||
<AddGlobalSecondaryIndexPanel {...addMaterializedViewPanelProps} />,
|
<AddMaterializedViewPanel {...addMaterializedViewPanelProps} />,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -214,10 +214,8 @@ export const Dialog: FC = () => {
|
|||||||
{contentHtml}
|
{contentHtml}
|
||||||
{progressIndicatorProps && <ProgressIndicator {...progressIndicatorProps} />}
|
{progressIndicatorProps && <ProgressIndicator {...progressIndicatorProps} />}
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<PrimaryButton {...primaryButtonProps} data-test={`DialogButton:${primaryButtonText}`} />
|
<PrimaryButton {...primaryButtonProps} />
|
||||||
{secondaryButtonProps && (
|
{secondaryButtonProps && <DefaultButton {...secondaryButtonProps} />}
|
||||||
<DefaultButton {...secondaryButtonProps} data-test={`DialogButton:${secondaryButtonText}`} />
|
|
||||||
)}
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</FluentDialog>
|
</FluentDialog>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -193,7 +193,6 @@ export const InputDataList: FC<InputDataListProps> = ({
|
|||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
id="filterInput"
|
id="filterInput"
|
||||||
data-test={"DocumentsTab/FilterInput"}
|
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AuthType } from "AuthType";
|
import { AuthType } from "AuthType";
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
|
import { Features } from "Platform/Hosted/extractFeatures";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
||||||
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
||||||
@@ -252,7 +253,7 @@ describe("SettingsComponent", () => {
|
|||||||
it("should save throughput bucket changes when Save button is clicked", async () => {
|
it("should save throughput bucket changes when Save button is clicked", async () => {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
apiType: "SQL",
|
apiType: "SQL",
|
||||||
throughputBucketsEnabled: true,
|
features: { enableThroughputBuckets: true } as Features,
|
||||||
authType: AuthType.AAD,
|
authType: AuthType.AAD,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import {
|
|||||||
ThroughputBucketsComponentProps,
|
ThroughputBucketsComponentProps,
|
||||||
} from "Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputBucketsComponent";
|
} from "Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputBucketsComponent";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
import { isFullTextSearchEnabled, isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||||
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
|
||||||
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import DiscardIcon from "../../../../images/discard.svg";
|
import DiscardIcon from "../../../../images/discard.svg";
|
||||||
@@ -45,11 +44,11 @@ import {
|
|||||||
ConflictResolutionComponent,
|
ConflictResolutionComponent,
|
||||||
ConflictResolutionComponentProps,
|
ConflictResolutionComponentProps,
|
||||||
} from "./SettingsSubComponents/ConflictResolutionComponent";
|
} from "./SettingsSubComponents/ConflictResolutionComponent";
|
||||||
import {
|
|
||||||
GlobalSecondaryIndexComponent,
|
|
||||||
GlobalSecondaryIndexComponentProps,
|
|
||||||
} from "./SettingsSubComponents/GlobalSecondaryIndexComponent";
|
|
||||||
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent";
|
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent";
|
||||||
|
import {
|
||||||
|
MaterializedViewComponent,
|
||||||
|
MaterializedViewComponentProps,
|
||||||
|
} from "./SettingsSubComponents/MaterializedViewComponent";
|
||||||
import {
|
import {
|
||||||
MongoIndexingPolicyComponent,
|
MongoIndexingPolicyComponent,
|
||||||
MongoIndexingPolicyComponentProps,
|
MongoIndexingPolicyComponentProps,
|
||||||
@@ -167,7 +166,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
private shouldShowComputedPropertiesEditor: boolean;
|
private shouldShowComputedPropertiesEditor: boolean;
|
||||||
private shouldShowIndexingPolicyEditor: boolean;
|
private shouldShowIndexingPolicyEditor: boolean;
|
||||||
private shouldShowPartitionKeyEditor: boolean;
|
private shouldShowPartitionKeyEditor: boolean;
|
||||||
private isGlobalSecondaryIndex: boolean;
|
private isMaterializedView: boolean;
|
||||||
private isVectorSearchEnabled: boolean;
|
private isVectorSearchEnabled: boolean;
|
||||||
private isFullTextSearchEnabled: boolean;
|
private isFullTextSearchEnabled: boolean;
|
||||||
private totalThroughputUsed: number;
|
private totalThroughputUsed: number;
|
||||||
@@ -185,13 +184,16 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
this.shouldShowComputedPropertiesEditor = userContext.apiType === "SQL";
|
this.shouldShowComputedPropertiesEditor = userContext.apiType === "SQL";
|
||||||
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
|
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
|
||||||
this.shouldShowPartitionKeyEditor = userContext.apiType === "SQL" && isRunningOnPublicCloud();
|
this.shouldShowPartitionKeyEditor = userContext.apiType === "SQL" && isRunningOnPublicCloud();
|
||||||
this.isGlobalSecondaryIndex =
|
this.isMaterializedView =
|
||||||
!!this.collection?.materializedViewDefinition() || !!this.collection?.materializedViews();
|
!!this.collection?.materializedViewDefinition() || !!this.collection?.materializedViews();
|
||||||
this.isVectorSearchEnabled = isVectorSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
|
this.isVectorSearchEnabled = isVectorSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
|
||||||
this.isFullTextSearchEnabled = userContext.apiType === "SQL";
|
this.isFullTextSearchEnabled = isFullTextSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
|
||||||
|
|
||||||
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
||||||
this.throughputBucketsEnabled = userContext.throughputBucketsEnabled;
|
this.throughputBucketsEnabled =
|
||||||
|
userContext.apiType === "SQL" &&
|
||||||
|
userContext.features.enableThroughputBuckets &&
|
||||||
|
userContext.authType === AuthType.AAD;
|
||||||
|
|
||||||
// Mongo container with system partition key still treat as "Fixed"
|
// Mongo container with system partition key still treat as "Fixed"
|
||||||
this.isFixedContainer =
|
this.isFixedContainer =
|
||||||
@@ -1071,11 +1073,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
databaseId: this.collection.databaseId,
|
databaseId: this.collection.databaseId,
|
||||||
collectionId: this.collection.id(),
|
collectionId: this.collection.id(),
|
||||||
currentOffer: this.collection.offer(),
|
currentOffer: this.collection.offer(),
|
||||||
autopilotThroughput: this.collection.offer?.()?.autoscaleMaxThroughput
|
autopilotThroughput: this.collection.offer().autoscaleMaxThroughput
|
||||||
? this.collection.offer?.()?.autoscaleMaxThroughput
|
? this.collection.offer().autoscaleMaxThroughput
|
||||||
: undefined,
|
: undefined,
|
||||||
manualThroughput: this.collection.offer?.()?.manualThroughput
|
manualThroughput: this.collection.offer().manualThroughput
|
||||||
? this.collection.offer?.()?.manualThroughput
|
? this.collection.offer().manualThroughput
|
||||||
: undefined,
|
: undefined,
|
||||||
throughputBuckets: this.state.throughputBuckets,
|
throughputBuckets: this.state.throughputBuckets,
|
||||||
});
|
});
|
||||||
@@ -1091,7 +1093,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
currentOffer: this.collection.offer(),
|
currentOffer: this.collection.offer(),
|
||||||
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
|
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
|
||||||
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
|
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
|
||||||
throughputBuckets: this.throughputBucketsEnabled ? this.state.throughputBuckets : undefined,
|
|
||||||
};
|
};
|
||||||
if (this.hasProvisioningTypeChanged()) {
|
if (this.hasProvisioningTypeChanged()) {
|
||||||
if (this.state.isAutoPilotSelected) {
|
if (this.state.isAutoPilotSelected) {
|
||||||
@@ -1147,7 +1148,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
collection: this.collection,
|
collection: this.collection,
|
||||||
database: this.database,
|
database: this.database,
|
||||||
isFixedContainer: this.isFixedContainer,
|
isFixedContainer: this.isFixedContainer,
|
||||||
isGlobalSecondaryIndex: this.isGlobalSecondaryIndex,
|
|
||||||
onThroughputChange: this.onThroughputChange,
|
onThroughputChange: this.onThroughputChange,
|
||||||
throughput: this.state.throughput,
|
throughput: this.state.throughput,
|
||||||
throughputBaseline: this.state.throughputBaseline,
|
throughputBaseline: this.state.throughputBaseline,
|
||||||
@@ -1213,7 +1213,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
isFullTextSearchEnabled: this.isFullTextSearchEnabled,
|
isFullTextSearchEnabled: this.isFullTextSearchEnabled,
|
||||||
shouldDiscardContainerPolicies: this.state.shouldDiscardContainerPolicies,
|
shouldDiscardContainerPolicies: this.state.shouldDiscardContainerPolicies,
|
||||||
resetShouldDiscardContainerPolicyChange: this.resetShouldDiscardContainerPolicies,
|
resetShouldDiscardContainerPolicyChange: this.resetShouldDiscardContainerPolicies,
|
||||||
isGlobalSecondaryIndex: this.isGlobalSecondaryIndex,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const indexingPolicyComponentProps: IndexingPolicyComponentProps = {
|
const indexingPolicyComponentProps: IndexingPolicyComponentProps = {
|
||||||
@@ -1278,10 +1277,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
database: useDatabases.getState().findDatabaseWithId(this.collection.databaseId),
|
database: useDatabases.getState().findDatabaseWithId(this.collection.databaseId),
|
||||||
collection: this.collection,
|
collection: this.collection,
|
||||||
explorer: this.props.settingsTab.getContainer(),
|
explorer: this.props.settingsTab.getContainer(),
|
||||||
isReadOnly: isFabricNative(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const globalSecondaryIndexComponentProps: GlobalSecondaryIndexComponentProps = {
|
const materializedViewComponentProps: MaterializedViewComponentProps = {
|
||||||
collection: this.collection,
|
collection: this.collection,
|
||||||
explorer: this.props.settingsTab.getContainer(),
|
explorer: this.props.settingsTab.getContainer(),
|
||||||
};
|
};
|
||||||
@@ -1342,17 +1340,17 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.throughputBucketsEnabled && !hasDatabaseSharedThroughput(this.collection) && this.offer) {
|
if (this.throughputBucketsEnabled) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
tab: SettingsV2TabTypes.ThroughputBucketsTab,
|
tab: SettingsV2TabTypes.ThroughputBucketsTab,
|
||||||
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,
|
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isGlobalSecondaryIndex) {
|
if (this.isMaterializedView) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
tab: SettingsV2TabTypes.GlobalSecondaryIndexTab,
|
tab: SettingsV2TabTypes.MaterializedViewTab,
|
||||||
content: <GlobalSecondaryIndexComponent {...globalSecondaryIndexComponentProps} />,
|
content: <MaterializedViewComponent {...materializedViewComponentProps} />,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export interface ContainerPolicyComponentProps {
|
|||||||
isFullTextSearchEnabled: boolean;
|
isFullTextSearchEnabled: boolean;
|
||||||
shouldDiscardContainerPolicies: boolean;
|
shouldDiscardContainerPolicies: boolean;
|
||||||
resetShouldDiscardContainerPolicyChange: () => void;
|
resetShouldDiscardContainerPolicyChange: () => void;
|
||||||
isGlobalSecondaryIndex?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> = ({
|
export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> = ({
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import { shallow } from "enzyme";
|
|
||||||
import React from "react";
|
|
||||||
import { collection, container } from "../TestUtils";
|
|
||||||
import { GlobalSecondaryIndexComponent } from "./GlobalSecondaryIndexComponent";
|
|
||||||
import { GlobalSecondaryIndexSourceComponent } from "./GlobalSecondaryIndexSourceComponent";
|
|
||||||
import { GlobalSecondaryIndexTargetComponent } from "./GlobalSecondaryIndexTargetComponent";
|
|
||||||
|
|
||||||
describe("GlobalSecondaryIndexComponent", () => {
|
|
||||||
let testCollection: typeof collection;
|
|
||||||
let testExplorer: typeof container;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
testCollection = { ...collection };
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders only the source component when materializedViewDefinition is missing", () => {
|
|
||||||
testCollection.materializedViews([
|
|
||||||
{ id: "view1", _rid: "rid1" },
|
|
||||||
{ id: "view2", _rid: "rid2" },
|
|
||||||
]);
|
|
||||||
testCollection.materializedViewDefinition(null);
|
|
||||||
const wrapper = shallow(<GlobalSecondaryIndexComponent collection={testCollection} explorer={testExplorer} />);
|
|
||||||
expect(wrapper.find(GlobalSecondaryIndexSourceComponent).exists()).toBe(true);
|
|
||||||
expect(wrapper.find(GlobalSecondaryIndexTargetComponent).exists()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders only the target component when materializedViews is missing", () => {
|
|
||||||
testCollection.materializedViews(null);
|
|
||||||
testCollection.materializedViewDefinition({
|
|
||||||
definition: "SELECT * FROM c WHERE c.id = 1",
|
|
||||||
sourceCollectionId: "source1",
|
|
||||||
sourceCollectionRid: "rid123",
|
|
||||||
});
|
|
||||||
const wrapper = shallow(<GlobalSecondaryIndexComponent collection={testCollection} explorer={testExplorer} />);
|
|
||||||
expect(wrapper.find(GlobalSecondaryIndexSourceComponent).exists()).toBe(false);
|
|
||||||
expect(wrapper.find(GlobalSecondaryIndexTargetComponent).exists()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders neither component when both are missing", () => {
|
|
||||||
testCollection.materializedViews(null);
|
|
||||||
testCollection.materializedViewDefinition(null);
|
|
||||||
const wrapper = shallow(<GlobalSecondaryIndexComponent collection={testCollection} explorer={testExplorer} />);
|
|
||||||
expect(wrapper.find(GlobalSecondaryIndexSourceComponent).exists()).toBe(false);
|
|
||||||
expect(wrapper.find(GlobalSecondaryIndexTargetComponent).exists()).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { shallow } from "enzyme";
|
||||||
|
import React from "react";
|
||||||
|
import { collection, container } from "../TestUtils";
|
||||||
|
import { MaterializedViewComponent } from "./MaterializedViewComponent";
|
||||||
|
import { MaterializedViewSourceComponent } from "./MaterializedViewSourceComponent";
|
||||||
|
import { MaterializedViewTargetComponent } from "./MaterializedViewTargetComponent";
|
||||||
|
|
||||||
|
describe("MaterializedViewComponent", () => {
|
||||||
|
let testCollection: typeof collection;
|
||||||
|
let testExplorer: typeof container;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testCollection = { ...collection };
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders only the source component when materializedViewDefinition is missing", () => {
|
||||||
|
testCollection.materializedViews([
|
||||||
|
{ id: "view1", _rid: "rid1" },
|
||||||
|
{ id: "view2", _rid: "rid2" },
|
||||||
|
]);
|
||||||
|
testCollection.materializedViewDefinition(null);
|
||||||
|
const wrapper = shallow(<MaterializedViewComponent collection={testCollection} explorer={testExplorer} />);
|
||||||
|
expect(wrapper.find(MaterializedViewSourceComponent).exists()).toBe(true);
|
||||||
|
expect(wrapper.find(MaterializedViewTargetComponent).exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders only the target component when materializedViews is missing", () => {
|
||||||
|
testCollection.materializedViews(null);
|
||||||
|
testCollection.materializedViewDefinition({
|
||||||
|
definition: "SELECT * FROM c WHERE c.id = 1",
|
||||||
|
sourceCollectionId: "source1",
|
||||||
|
sourceCollectionRid: "rid123",
|
||||||
|
});
|
||||||
|
const wrapper = shallow(<MaterializedViewComponent collection={testCollection} explorer={testExplorer} />);
|
||||||
|
expect(wrapper.find(MaterializedViewSourceComponent).exists()).toBe(false);
|
||||||
|
expect(wrapper.find(MaterializedViewTargetComponent).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders neither component when both are missing", () => {
|
||||||
|
testCollection.materializedViews(null);
|
||||||
|
testCollection.materializedViewDefinition(null);
|
||||||
|
const wrapper = shallow(<MaterializedViewComponent collection={testCollection} explorer={testExplorer} />);
|
||||||
|
expect(wrapper.find(MaterializedViewSourceComponent).exists()).toBe(false);
|
||||||
|
expect(wrapper.find(MaterializedViewTargetComponent).exists()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,27 +2,22 @@ import { FontIcon, Link, Stack, Text } from "@fluentui/react";
|
|||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
import { GlobalSecondaryIndexSourceComponent } from "./GlobalSecondaryIndexSourceComponent";
|
import { MaterializedViewSourceComponent } from "./MaterializedViewSourceComponent";
|
||||||
import { GlobalSecondaryIndexTargetComponent } from "./GlobalSecondaryIndexTargetComponent";
|
import { MaterializedViewTargetComponent } from "./MaterializedViewTargetComponent";
|
||||||
|
|
||||||
export interface GlobalSecondaryIndexComponentProps {
|
export interface MaterializedViewComponentProps {
|
||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GlobalSecondaryIndexComponent: React.FC<GlobalSecondaryIndexComponentProps> = ({
|
export const MaterializedViewComponent: React.FC<MaterializedViewComponentProps> = ({ collection, explorer }) => {
|
||||||
collection,
|
|
||||||
explorer,
|
|
||||||
}) => {
|
|
||||||
const isTargetContainer = !!collection?.materializedViewDefinition();
|
const isTargetContainer = !!collection?.materializedViewDefinition();
|
||||||
const isSourceContainer = !!collection?.materializedViews();
|
const isSourceContainer = !!collection?.materializedViews();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack tokens={{ childrenGap: 8 }} styles={{ root: { maxWidth: 600 } }}>
|
<Stack tokens={{ childrenGap: 8 }} styles={{ root: { maxWidth: 600 } }}>
|
||||||
<Stack horizontal verticalAlign="center" wrap tokens={{ childrenGap: 8 }}>
|
<Stack horizontal verticalAlign="center" wrap tokens={{ childrenGap: 8 }}>
|
||||||
{isSourceContainer && (
|
<Text styles={{ root: { fontWeight: 600 } }}>This container has the following views defined for it.</Text>
|
||||||
<Text styles={{ root: { fontWeight: 600 } }}>This container has the following indexes defined for it.</Text>
|
|
||||||
)}
|
|
||||||
<Text>
|
<Text>
|
||||||
<Link
|
<Link
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -31,11 +26,11 @@ export const GlobalSecondaryIndexComponent: React.FC<GlobalSecondaryIndexCompone
|
|||||||
Learn more
|
Learn more
|
||||||
<FontIcon iconName="NavigateExternalInline" style={{ marginLeft: "4px" }} />
|
<FontIcon iconName="NavigateExternalInline" style={{ marginLeft: "4px" }} />
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
about how to define global secondary indexes and how to use them.
|
about how to define materialized views and how to use them.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
{isSourceContainer && <GlobalSecondaryIndexSourceComponent collection={collection} explorer={explorer} />}
|
{isSourceContainer && <MaterializedViewSourceComponent collection={collection} explorer={explorer} />}
|
||||||
{isTargetContainer && <GlobalSecondaryIndexTargetComponent collection={collection} />}
|
{isTargetContainer && <MaterializedViewTargetComponent collection={collection} />}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -2,9 +2,9 @@ import { PrimaryButton } from "@fluentui/react";
|
|||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { collection, container } from "../TestUtils";
|
import { collection, container } from "../TestUtils";
|
||||||
import { GlobalSecondaryIndexSourceComponent } from "./GlobalSecondaryIndexSourceComponent";
|
import { MaterializedViewSourceComponent } from "./MaterializedViewSourceComponent";
|
||||||
|
|
||||||
describe("GlobalSecondaryIndexSourceComponent", () => {
|
describe("MaterializedViewSourceComponent", () => {
|
||||||
let testCollection: typeof collection;
|
let testCollection: typeof collection;
|
||||||
let testExplorer: typeof container;
|
let testExplorer: typeof container;
|
||||||
|
|
||||||
@@ -13,23 +13,17 @@ describe("GlobalSecondaryIndexSourceComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("renders without crashing", () => {
|
it("renders without crashing", () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(<MaterializedViewSourceComponent collection={testCollection} explorer={testExplorer} />);
|
||||||
<GlobalSecondaryIndexSourceComponent collection={testCollection} explorer={testExplorer} />,
|
|
||||||
);
|
|
||||||
expect(wrapper.exists()).toBe(true);
|
expect(wrapper.exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the PrimaryButton", () => {
|
it("renders the PrimaryButton", () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(<MaterializedViewSourceComponent collection={testCollection} explorer={testExplorer} />);
|
||||||
<GlobalSecondaryIndexSourceComponent collection={testCollection} explorer={testExplorer} />,
|
|
||||||
);
|
|
||||||
expect(wrapper.find(PrimaryButton).exists()).toBe(true);
|
expect(wrapper.find(PrimaryButton).exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates when new global secondary indexes are provided", () => {
|
it("updates when new materialized views are provided", () => {
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(<MaterializedViewSourceComponent collection={testCollection} explorer={testExplorer} />);
|
||||||
<GlobalSecondaryIndexSourceComponent collection={testCollection} explorer={testExplorer} />,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Simulating an update by modifying the observable directly
|
// Simulating an update by modifying the observable directly
|
||||||
testCollection.materializedViews([{ id: "view3", _rid: "rid3" }]);
|
testCollection.materializedViews([{ id: "view3", _rid: "rid3" }]);
|
||||||
@@ -1,30 +1,29 @@
|
|||||||
import { PrimaryButton } from "@fluentui/react";
|
import { PrimaryButton } from "@fluentui/react";
|
||||||
import { GlobalSecondaryIndexLabels } from "Common/Constants";
|
import { MaterializedViewsLabels } from "Common/Constants";
|
||||||
import { MaterializedView } from "Contracts/DataModels";
|
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { loadMonaco } from "Explorer/LazyMonaco";
|
import { loadMonaco } from "Explorer/LazyMonaco";
|
||||||
import { AddGlobalSecondaryIndexPanel } from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
import { AddMaterializedViewPanel } from "Explorer/Panes/AddMaterializedViewPanel/AddMaterializedViewPanel";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
|
||||||
export interface GlobalSecondaryIndexSourceComponentProps {
|
export interface MaterializedViewSourceComponentProps {
|
||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexSourceComponentProps> = ({
|
export const MaterializedViewSourceComponent: React.FC<MaterializedViewSourceComponentProps> = ({
|
||||||
collection,
|
collection,
|
||||||
explorer,
|
explorer,
|
||||||
}) => {
|
}) => {
|
||||||
const editorContainerRef = useRef<HTMLDivElement>(null);
|
const editorContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>(null);
|
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>(null);
|
||||||
|
|
||||||
const globalSecondaryIndexes: MaterializedView[] = collection?.materializedViews() ?? [];
|
const materializedViews = collection?.materializedViews() ?? [];
|
||||||
|
|
||||||
// Helper function to fetch the definition and partition key of targetContainer by traversing through all collections and matching id from MaterializedView[] with collection id.
|
// Helper function to fetch the definition and partition key of targetContainer by traversing through all collections and matching id from MaterializedViews[] with collection id.
|
||||||
const getViewDetails = (viewId: string): { definition: string; partitionKey: string[] } => {
|
const getViewDetails = (viewId: string): { definition: string; partitionKey: string[] } => {
|
||||||
let definition = "";
|
let definition = "";
|
||||||
let partitionKey: string[] = [];
|
let partitionKey: string[] = [];
|
||||||
@@ -32,8 +31,8 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
|||||||
useDatabases.getState().databases.find((database) => {
|
useDatabases.getState().databases.find((database) => {
|
||||||
const collection = database.collections().find((collection) => collection.id() === viewId);
|
const collection = database.collections().find((collection) => collection.id() === viewId);
|
||||||
if (collection) {
|
if (collection) {
|
||||||
const globalSecondaryIndexDefinition = collection.materializedViewDefinition();
|
const materializedViewDefinition = collection.materializedViewDefinition();
|
||||||
globalSecondaryIndexDefinition && (definition = globalSecondaryIndexDefinition.definition);
|
materializedViewDefinition && (definition = materializedViewDefinition.definition);
|
||||||
collection.partitionKey?.paths && (partitionKey = collection.partitionKey.paths);
|
collection.partitionKey?.paths && (partitionKey = collection.partitionKey.paths);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -43,7 +42,7 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
|||||||
|
|
||||||
//JSON value for the editor using the fetched id and definitions.
|
//JSON value for the editor using the fetched id and definitions.
|
||||||
const jsonValue = JSON.stringify(
|
const jsonValue = JSON.stringify(
|
||||||
globalSecondaryIndexes.map((view) => {
|
materializedViews.map((view) => {
|
||||||
const { definition, partitionKey } = getViewDetails(view.id);
|
const { definition, partitionKey } = getViewDetails(view.id);
|
||||||
return {
|
return {
|
||||||
name: view.id,
|
name: view.id,
|
||||||
@@ -67,7 +66,7 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
|||||||
editorRef.current = monacoInstance.editor.create(editorContainerRef.current, {
|
editorRef.current = monacoInstance.editor.create(editorContainerRef.current, {
|
||||||
value: jsonValue,
|
value: jsonValue,
|
||||||
language: "json",
|
language: "json",
|
||||||
ariaLabel: "Global Secondary Index JSON",
|
ariaLabel: "Materialized Views JSON",
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -98,14 +97,14 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
text="Add index"
|
text="Add view"
|
||||||
styles={{ root: { width: "fit-content", marginTop: 12 } }}
|
styles={{ root: { width: "fit-content", marginTop: 12 } }}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
useSidePanel
|
useSidePanel
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel(
|
.openSidePanel(
|
||||||
GlobalSecondaryIndexLabels.NewGlobalSecondaryIndex,
|
MaterializedViewsLabels.NewMaterializedView,
|
||||||
<AddGlobalSecondaryIndexPanel explorer={explorer} sourceContainer={collection} />,
|
<AddMaterializedViewPanel explorer={explorer} sourceContainer={collection} />,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -3,9 +3,9 @@ import { Collection } from "Contracts/ViewModels";
|
|||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { collection } from "../TestUtils";
|
import { collection } from "../TestUtils";
|
||||||
import { GlobalSecondaryIndexTargetComponent } from "./GlobalSecondaryIndexTargetComponent";
|
import { MaterializedViewTargetComponent } from "./MaterializedViewTargetComponent";
|
||||||
|
|
||||||
describe("GlobalSecondaryIndexTargetComponent", () => {
|
describe("MaterializedViewTargetComponent", () => {
|
||||||
let testCollection: Collection;
|
let testCollection: Collection;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -16,17 +16,17 @@ describe("GlobalSecondaryIndexTargetComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("renders without crashing", () => {
|
it("renders without crashing", () => {
|
||||||
const wrapper = shallow(<GlobalSecondaryIndexTargetComponent collection={testCollection} />);
|
const wrapper = shallow(<MaterializedViewTargetComponent collection={testCollection} />);
|
||||||
expect(wrapper.exists()).toBe(true);
|
expect(wrapper.exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays the source container ID", () => {
|
it("displays the source container ID", () => {
|
||||||
const wrapper = shallow(<GlobalSecondaryIndexTargetComponent collection={testCollection} />);
|
const wrapper = shallow(<MaterializedViewTargetComponent collection={testCollection} />);
|
||||||
expect(wrapper.find(Text).at(2).dive().text()).toBe("source1");
|
expect(wrapper.find(Text).at(2).dive().text()).toBe("source1");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays the global secondary index definition", () => {
|
it("displays the materialized view definition", () => {
|
||||||
const wrapper = shallow(<GlobalSecondaryIndexTargetComponent collection={testCollection} />);
|
const wrapper = shallow(<MaterializedViewTargetComponent collection={testCollection} />);
|
||||||
expect(wrapper.find(Text).at(4).dive().text()).toBe("SELECT * FROM c WHERE c.id = 1");
|
expect(wrapper.find(Text).at(4).dive().text()).toBe("SELECT * FROM c WHERE c.id = 1");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2,14 +2,12 @@ import { Stack, Text } from "@fluentui/react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
|
||||||
export interface GlobalSecondaryIndexTargetComponentProps {
|
export interface MaterializedViewTargetComponentProps {
|
||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GlobalSecondaryIndexTargetComponent: React.FC<GlobalSecondaryIndexTargetComponentProps> = ({
|
export const MaterializedViewTargetComponent: React.FC<MaterializedViewTargetComponentProps> = ({ collection }) => {
|
||||||
collection,
|
const materializedViewDefinition = collection?.materializedViewDefinition();
|
||||||
}) => {
|
|
||||||
const globalSecondaryIndexDefinition = collection?.materializedViewDefinition();
|
|
||||||
|
|
||||||
const textHeadingStyle = {
|
const textHeadingStyle = {
|
||||||
root: { fontWeight: "600", fontSize: 16 },
|
root: { fontWeight: "600", fontSize: 16 },
|
||||||
@@ -25,19 +23,19 @@ export const GlobalSecondaryIndexTargetComponent: React.FC<GlobalSecondaryIndexT
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack tokens={{ childrenGap: 15 }} styles={{ root: { maxWidth: 600 } }}>
|
<Stack tokens={{ childrenGap: 15 }} styles={{ root: { maxWidth: 600 } }}>
|
||||||
<Text styles={textHeadingStyle}>Global Secondary Index Settings</Text>
|
<Text styles={textHeadingStyle}>Materialized View Settings</Text>
|
||||||
|
|
||||||
<Stack tokens={{ childrenGap: 5 }}>
|
<Stack tokens={{ childrenGap: 5 }}>
|
||||||
<Text styles={{ root: { fontWeight: "600" } }}>Source container</Text>
|
<Text styles={{ root: { fontWeight: "600" } }}>Source container</Text>
|
||||||
<Stack styles={valueBoxStyle}>
|
<Stack styles={valueBoxStyle}>
|
||||||
<Text>{globalSecondaryIndexDefinition?.sourceCollectionId}</Text>
|
<Text>{materializedViewDefinition?.sourceCollectionId}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack tokens={{ childrenGap: 5 }}>
|
<Stack tokens={{ childrenGap: 5 }}>
|
||||||
<Text styles={{ root: { fontWeight: "600" } }}>Global secondary index definition</Text>
|
<Text styles={{ root: { fontWeight: "600" } }}>Materialized view definition</Text>
|
||||||
<Stack styles={valueBoxStyle}>
|
<Stack styles={valueBoxStyle}>
|
||||||
<Text>{globalSecondaryIndexDefinition?.definition}</Text>
|
<Text>{materializedViewDefinition?.definition}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { shallow } from "enzyme";
|
|
||||||
import {
|
|
||||||
PartitionKeyComponent,
|
|
||||||
PartitionKeyComponentProps,
|
|
||||||
} from "Explorer/Controls/Settings/SettingsSubComponents/PartitionKeyComponent";
|
|
||||||
import Explorer from "Explorer/Explorer";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
describe("PartitionKeyComponent", () => {
|
|
||||||
// Create a test setup function to get fresh instances for each test
|
|
||||||
const setupTest = () => {
|
|
||||||
// Create an instance of the mocked Explorer
|
|
||||||
const explorer = new Explorer();
|
|
||||||
// Create minimal mock objects for database and collection
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const mockDatabase = {} as any as import("../../../../Contracts/ViewModels").Database;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const mockCollection = {} as any as import("../../../../Contracts/ViewModels").Collection;
|
|
||||||
|
|
||||||
// Create props with the mocked Explorer instance
|
|
||||||
const props: PartitionKeyComponentProps = {
|
|
||||||
database: mockDatabase,
|
|
||||||
collection: mockCollection,
|
|
||||||
explorer,
|
|
||||||
};
|
|
||||||
|
|
||||||
return { explorer, props };
|
|
||||||
};
|
|
||||||
|
|
||||||
it("renders default component and matches snapshot", () => {
|
|
||||||
const { props } = setupTest();
|
|
||||||
const wrapper = shallow(<PartitionKeyComponent {...props} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("renders read-only component and matches snapshot", () => {
|
|
||||||
const { props } = setupTest();
|
|
||||||
const wrapper = shallow(<PartitionKeyComponent {...props} isReadOnly={true} />);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -29,26 +29,16 @@ export interface PartitionKeyComponentProps {
|
|||||||
database: ViewModels.Database;
|
database: ViewModels.Database;
|
||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
isReadOnly?: boolean; // true: cannot change partition key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({ database, collection, explorer }) => {
|
||||||
database,
|
|
||||||
collection,
|
|
||||||
explorer,
|
|
||||||
isReadOnly,
|
|
||||||
}) => {
|
|
||||||
const { dataTransferJobs } = useDataTransferJobs();
|
const { dataTransferJobs } = useDataTransferJobs();
|
||||||
const [portalDataTransferJob, setPortalDataTransferJob] = React.useState<DataTransferJobGetResults>(null);
|
const [portalDataTransferJob, setPortalDataTransferJob] = React.useState<DataTransferJobGetResults>(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isReadOnly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadDataTransferJobs = refreshDataTransferOperations;
|
const loadDataTransferJobs = refreshDataTransferOperations;
|
||||||
loadDataTransferJobs();
|
loadDataTransferJobs();
|
||||||
}, [isReadOnly]);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const currentJob = findPortalDataTransferJob();
|
const currentJob = findPortalDataTransferJob();
|
||||||
@@ -161,7 +151,7 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Stack tokens={{ childrenGap: 20 }} styles={{ root: { maxWidth: 600 } }}>
|
<Stack tokens={{ childrenGap: 20 }} styles={{ root: { maxWidth: 600 } }}>
|
||||||
<Stack tokens={{ childrenGap: 10 }}>
|
<Stack tokens={{ childrenGap: 10 }}>
|
||||||
{!isReadOnly && <Text styles={textHeadingStyle}>Change {partitionKeyName.toLowerCase()}</Text>}
|
<Text styles={textHeadingStyle}>Change {partitionKeyName.toLowerCase()}</Text>
|
||||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||||
<Stack tokens={{ childrenGap: 5 }}>
|
<Stack tokens={{ childrenGap: 5 }}>
|
||||||
<Text styles={textSubHeadingStyle}>Current {partitionKeyName.toLowerCase()}</Text>
|
<Text styles={textSubHeadingStyle}>Current {partitionKeyName.toLowerCase()}</Text>
|
||||||
@@ -173,12 +163,9 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{!isReadOnly && (
|
|
||||||
<>
|
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>
|
<MessageBar messageBarType={MessageBarType.warning}>
|
||||||
To safeguard the integrity of the data being copied to the new container, ensure that no updates are made to
|
To safeguard the integrity of the data being copied to the new container, ensure that no updates are made to the
|
||||||
the source container for the entire duration of the partition key change process.
|
source container for the entire duration of the partition key change process.
|
||||||
<Link
|
<Link
|
||||||
href="https://learn.microsoft.com/azure/cosmos-db/container-copy#how-does-container-copy-work"
|
href="https://learn.microsoft.com/azure/cosmos-db/container-copy#how-does-container-copy-work"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -188,8 +175,8 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
|||||||
</Link>
|
</Link>
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
<Text>
|
<Text>
|
||||||
To change the partition key, a new destination container must be created or an existing destination
|
To change the partition key, a new destination container must be created or an existing destination container
|
||||||
container selected. Data will then be copied to the destination container.
|
selected. Data will then be copied to the destination container.
|
||||||
</Text>
|
</Text>
|
||||||
{configContext.platform !== Platform.Emulator && (
|
{configContext.platform !== Platform.Emulator && (
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
@@ -227,8 +214,6 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ describe("ScaleComponent", () => {
|
|||||||
collection: collection,
|
collection: collection,
|
||||||
database: undefined,
|
database: undefined,
|
||||||
isFixedContainer: false,
|
isFixedContainer: false,
|
||||||
isGlobalSecondaryIndex: false,
|
|
||||||
onThroughputChange: () => {
|
onThroughputChange: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export interface ScaleComponentProps {
|
|||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
database: ViewModels.Database;
|
database: ViewModels.Database;
|
||||||
isFixedContainer: boolean;
|
isFixedContainer: boolean;
|
||||||
isGlobalSecondaryIndex: boolean;
|
|
||||||
onThroughputChange: (newThroughput: number) => void;
|
onThroughputChange: (newThroughput: number) => void;
|
||||||
throughput: number;
|
throughput: number;
|
||||||
throughputBaseline: number;
|
throughputBaseline: number;
|
||||||
@@ -144,7 +143,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
throughputError={this.props.throughputError}
|
throughputError={this.props.throughputError}
|
||||||
instantMaximumThroughput={this.offer?.instantMaximumThroughput}
|
instantMaximumThroughput={this.offer?.instantMaximumThroughput}
|
||||||
softAllowedMaximumThroughput={this.offer?.softAllowedMaximumThroughput}
|
softAllowedMaximumThroughput={this.offer?.softAllowedMaximumThroughput}
|
||||||
isGlobalSecondaryIndex={this.props.isGlobalSecondaryIndex}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -143,39 +143,4 @@ describe("SubSettingsComponent", () => {
|
|||||||
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
|
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
|
||||||
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
|
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 geospatialVisible: boolean;
|
||||||
private partitionKeyValue: string;
|
private partitionKeyValue: string;
|
||||||
private partitionKeyName: string;
|
private partitionKeyName: string;
|
||||||
private uniqueKeyName: string;
|
|
||||||
private uniqueKeyValue: string;
|
|
||||||
|
|
||||||
constructor(props: SubSettingsComponentProps) {
|
constructor(props: SubSettingsComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.geospatialVisible = userContext.apiType === "SQL";
|
this.geospatialVisible = userContext.apiType === "SQL";
|
||||||
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||||
this.partitionKeyValue = this.getPartitionKeyValue();
|
this.partitionKeyValue = this.getPartitionKeyValue();
|
||||||
this.uniqueKeyName = "Unique keys";
|
|
||||||
this.uniqueKeyValue = this.getUniqueKeyValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -355,28 +351,6 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
||||||
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
|
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 {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Stack {...subComponentStackProps}>
|
<Stack {...subComponentStackProps}>
|
||||||
@@ -389,8 +363,6 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
|
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
|
||||||
|
|
||||||
{this.getPartitionKeyComponent()}
|
{this.getPartitionKeyComponent()}
|
||||||
|
|
||||||
{this.getUniqueKeyComponent()}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
|
|
||||||
it("renders the correct number of buckets", () => {
|
it("renders the correct number of buckets", () => {
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} />);
|
render(<ThroughputBucketsComponent {...defaultProps} />);
|
||||||
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5);
|
expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders buckets in the correct order even if input is unordered", () => {
|
it("renders buckets in the correct order even if input is unordered", () => {
|
||||||
@@ -36,14 +36,8 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
];
|
];
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />);
|
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={unorderedBuckets} />);
|
||||||
|
|
||||||
const bucketLabels = screen.getAllByText(/Bucket \d+/).map((el) => el.textContent);
|
const bucketLabels = screen.getAllByText(/Group \d+/).map((el) => el.textContent);
|
||||||
expect(bucketLabels).toEqual([
|
expect(bucketLabels).toEqual(["Group 1 (Data Explorer Query Bucket)", "Group 2", "Group 3", "Group 4", "Group 5"]);
|
||||||
"Bucket 1 (Data Explorer Query Bucket)",
|
|
||||||
"Bucket 2",
|
|
||||||
"Bucket 3",
|
|
||||||
"Bucket 4",
|
|
||||||
"Bucket 5",
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders all provided buckets even if they exceed the max default bucket count", () => {
|
it("renders all provided buckets even if they exceed the max default bucket count", () => {
|
||||||
@@ -59,7 +53,7 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
|
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={oversizedBuckets} />);
|
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={oversizedBuckets} />);
|
||||||
|
|
||||||
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(7);
|
expect(screen.getAllByText(/Group \d+/)).toHaveLength(7);
|
||||||
|
|
||||||
expect(screen.getByDisplayValue("50")).toBeInTheDocument();
|
expect(screen.getByDisplayValue("50")).toBeInTheDocument();
|
||||||
expect(screen.getByDisplayValue("60")).toBeInTheDocument();
|
expect(screen.getByDisplayValue("60")).toBeInTheDocument();
|
||||||
@@ -177,7 +171,7 @@ describe("ThroughputBucketsComponent", () => {
|
|||||||
|
|
||||||
it("ensures default buckets are used when no buckets are provided", () => {
|
it("ensures default buckets are used when no buckets are provided", () => {
|
||||||
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />);
|
render(<ThroughputBucketsComponent {...defaultProps} currentBuckets={[]} />);
|
||||||
expect(screen.getAllByText(/Bucket \d+/)).toHaveLength(5);
|
expect(screen.getAllByText(/Group \d+/)).toHaveLength(5);
|
||||||
expect(screen.getAllByDisplayValue("100")).toHaveLength(5);
|
expect(screen.getAllByDisplayValue("100")).toHaveLength(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
|||||||
value={bucket.maxThroughputPercentage}
|
value={bucket.maxThroughputPercentage}
|
||||||
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
|
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
|
||||||
showValue={false}
|
showValue={false}
|
||||||
label={`Bucket ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
|
label={`Group ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
|
||||||
styles={{ root: { flex: 2, maxWidth: 400 } }}
|
styles={{ root: { flex: 2, maxWidth: 400 } }}
|
||||||
disabled={bucket.maxThroughputPercentage === 100}
|
disabled={bucket.maxThroughputPercentage === 100}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ describe("ThroughputInputAutoPilotV3Component", () => {
|
|||||||
},
|
},
|
||||||
instantMaximumThroughput: 5000,
|
instantMaximumThroughput: 5000,
|
||||||
softAllowedMaximumThroughput: 1000000,
|
softAllowedMaximumThroughput: 1000000,
|
||||||
isGlobalSecondaryIndex: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("throughput input visible", () => {
|
it("throughput input visible", () => {
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ export interface ThroughputInputAutoPilotV3Props {
|
|||||||
throughputError?: string;
|
throughputError?: string;
|
||||||
instantMaximumThroughput: number;
|
instantMaximumThroughput: number;
|
||||||
softAllowedMaximumThroughput: number;
|
softAllowedMaximumThroughput: number;
|
||||||
isGlobalSecondaryIndex: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ThroughputInputAutoPilotV3State {
|
interface ThroughputInputAutoPilotV3State {
|
||||||
@@ -285,7 +284,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
serverId,
|
serverId,
|
||||||
numberOfRegions,
|
numberOfRegions,
|
||||||
isMultimaster,
|
isMultimaster,
|
||||||
false,
|
true,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -376,8 +375,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
toolTipElement={getToolTipContainer(this.props.infoBubbleText)}
|
toolTipElement={getToolTipContainer(this.props.infoBubbleText)}
|
||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
{!this.props.isGlobalSecondaryIndex && (
|
|
||||||
<>
|
|
||||||
{this.overrideWithProvisionedThroughputSettings() && (
|
{this.overrideWithProvisionedThroughputSettings() && (
|
||||||
<MessageBar
|
<MessageBar
|
||||||
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
||||||
@@ -394,8 +391,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
ariaLabelledBy={labelId}
|
ariaLabelledBy={labelId}
|
||||||
styles={getChoiceGroupStyles(this.props.wasAutopilotOriginallySet, this.props.isAutoPilotSelected, true)}
|
styles={getChoiceGroupStyles(this.props.wasAutopilotOriginallySet, this.props.isAutoPilotSelected, true)}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -559,69 +554,16 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
private getThroughputTextField = (): JSX.Element => (
|
private getThroughputTextField = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
{this.props.isAutoPilotSelected ? (
|
{this.props.isAutoPilotSelected ? (
|
||||||
<Stack horizontal verticalAlign="end" tokens={{ childrenGap: 8 }}>
|
|
||||||
{/* Column 1: Minimum RU/s */}
|
|
||||||
<Stack tokens={{ childrenGap: 4 }}>
|
|
||||||
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}>
|
|
||||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
|
||||||
Minimum RU/s
|
|
||||||
</Text>
|
|
||||||
<FontIcon iconName="Info" style={{ fontSize: 12, color: "#666" }} />
|
|
||||||
</Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "Segoe UI",
|
|
||||||
width: 70,
|
|
||||||
height: 28,
|
|
||||||
border: "none",
|
|
||||||
fontSize: 14,
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
fontWeight: 400,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
boxSizing: "border-box",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(this.props.maxAutoPilotThroughput)}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{/* Column 2: "x 10 =" Text */}
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "Segoe UI",
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 400,
|
|
||||||
paddingBottom: 6,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
x 10 =
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{/* Column 3: Maximum RU/s */}
|
|
||||||
<Stack tokens={{ childrenGap: 4 }}>
|
|
||||||
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}>
|
|
||||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
|
||||||
Maximum RU/s
|
|
||||||
</Text>
|
|
||||||
<FontIcon iconName="Info" style={{ fontSize: 12, color: "#666" }} />
|
|
||||||
</Stack>
|
|
||||||
<TextField
|
<TextField
|
||||||
|
label="Maximum RU/s required by this resource"
|
||||||
required
|
required
|
||||||
type="number"
|
type="number"
|
||||||
id="autopilotInput"
|
id="autopilotInput"
|
||||||
key="auto pilot throughput input"
|
key="auto pilot throughput input"
|
||||||
styles={{
|
styles={getTextFieldStyles(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)}
|
||||||
...getTextFieldStyles(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline),
|
|
||||||
fieldGroup: { width: 100, height: 28 },
|
|
||||||
field: { fontSize: 14, fontWeight: 400 },
|
|
||||||
}}
|
|
||||||
disabled={this.overrideWithProvisionedThroughputSettings()}
|
disabled={this.overrideWithProvisionedThroughputSettings()}
|
||||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||||
value={
|
value={this.overrideWithProvisionedThroughputSettings() ? "" : this.props.maxAutoPilotThroughput?.toString()}
|
||||||
this.overrideWithProvisionedThroughputSettings() ? "" : this.props.maxAutoPilotThroughput?.toString()
|
|
||||||
}
|
|
||||||
onChange={this.onAutoPilotThroughputChange}
|
onChange={this.onAutoPilotThroughputChange}
|
||||||
min={autoPilotThroughput1K}
|
min={autoPilotThroughput1K}
|
||||||
onGetErrorMessage={(value: string) => {
|
onGetErrorMessage={(value: string) => {
|
||||||
@@ -632,8 +574,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
}}
|
}}
|
||||||
validateOnLoad={false}
|
validateOnLoad={false}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
) : (
|
) : (
|
||||||
<TextField
|
<TextField
|
||||||
required
|
required
|
||||||
|
|||||||
@@ -157,125 +157,11 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalAlign="end"
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalAlign="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"fontWeight": 600,
|
|
||||||
"lineHeight": "20px",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
variant="small"
|
|
||||||
>
|
|
||||||
Minimum RU/s
|
|
||||||
</Text>
|
|
||||||
<FontIcon
|
|
||||||
iconName="Info"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"color": "#666",
|
|
||||||
"fontSize": 12,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"alignItems": "center",
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"border": "none",
|
|
||||||
"boxSizing": "border-box",
|
|
||||||
"display": "flex",
|
|
||||||
"fontFamily": "Segoe UI",
|
|
||||||
"fontSize": 14,
|
|
||||||
"fontWeight": 400,
|
|
||||||
"height": 28,
|
|
||||||
"justifyContent": "center",
|
|
||||||
"width": 70,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
400
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"fontFamily": "Segoe UI",
|
|
||||||
"fontSize": 12,
|
|
||||||
"fontWeight": 400,
|
|
||||||
"paddingBottom": 6,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
x 10 =
|
|
||||||
</Text>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalAlign="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"fontWeight": 600,
|
|
||||||
"lineHeight": "20px",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
variant="small"
|
|
||||||
>
|
|
||||||
Maximum RU/s
|
|
||||||
</Text>
|
|
||||||
<FontIcon
|
|
||||||
iconName="Info"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"color": "#666",
|
|
||||||
"fontSize": 12,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
disabled={true}
|
disabled={true}
|
||||||
id="autopilotInput"
|
id="autopilotInput"
|
||||||
key="auto pilot throughput input"
|
key="auto pilot throughput input"
|
||||||
|
label="Maximum RU/s required by this resource"
|
||||||
min={1000}
|
min={1000}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onGetErrorMessage={[Function]}
|
onGetErrorMessage={[Function]}
|
||||||
@@ -283,13 +169,16 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
step={1000}
|
step={1000}
|
||||||
styles={
|
styles={
|
||||||
{
|
{
|
||||||
"field": {
|
|
||||||
"fontSize": 14,
|
|
||||||
"fontWeight": 400,
|
|
||||||
},
|
|
||||||
"fieldGroup": {
|
"fieldGroup": {
|
||||||
"height": 28,
|
"borderColor": "",
|
||||||
"width": 100,
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,8 +186,6 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
validateOnLoad={false}
|
validateOnLoad={false}
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack
|
<Stack
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`PartitionKeyComponent renders default component and matches snapshot 1`] = `
|
|
||||||
<Stack
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 10,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Change
|
|
||||||
partition key
|
|
||||||
</Text>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"fontWeight": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Current
|
|
||||||
partition key
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"fontWeight": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Partitioning
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text />
|
|
||||||
<Text>
|
|
||||||
Non-hierarchical
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
<StyledMessageBar
|
|
||||||
messageBarType={5}
|
|
||||||
>
|
|
||||||
To safeguard the integrity of the data being copied to the new container, ensure that no updates are made to the source container for the entire duration of the partition key change process.
|
|
||||||
<StyledLinkBase
|
|
||||||
href="https://learn.microsoft.com/azure/cosmos-db/container-copy#how-does-container-copy-work"
|
|
||||||
target="_blank"
|
|
||||||
underline={true}
|
|
||||||
>
|
|
||||||
Learn more
|
|
||||||
</StyledLinkBase>
|
|
||||||
</StyledMessageBar>
|
|
||||||
<Text>
|
|
||||||
To change the partition key, a new destination container must be created or an existing destination container selected. Data will then be copied to the destination container.
|
|
||||||
</Text>
|
|
||||||
<CustomizedPrimaryButton
|
|
||||||
onClick={[Function]}
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"width": "fit-content",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text="Change"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`PartitionKeyComponent renders read-only component and matches snapshot 1`] = `
|
|
||||||
<Stack
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"maxWidth": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 10,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"fontWeight": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Current
|
|
||||||
partition key
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"fontWeight": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Partitioning
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
|
||||||
tokens={
|
|
||||||
{
|
|
||||||
"childrenGap": 5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text />
|
|
||||||
<Text>
|
|
||||||
Non-hierarchical
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
`;
|
|
||||||
@@ -231,34 +231,6 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</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>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -548,34 +520,6 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</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>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -825,34 +769,6 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</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>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1167,34 +1083,6 @@ exports[`SubSettingsComponent renders 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</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>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1483,33 +1371,5 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</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>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
|
|
||||||
import { MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
import { MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||||
|
|
||||||
const zeroValue = 0;
|
const zeroValue = 0;
|
||||||
@@ -58,7 +57,7 @@ export enum SettingsV2TabTypes {
|
|||||||
ComputedPropertiesTab,
|
ComputedPropertiesTab,
|
||||||
ContainerVectorPolicyTab,
|
ContainerVectorPolicyTab,
|
||||||
ThroughputBucketsTab,
|
ThroughputBucketsTab,
|
||||||
GlobalSecondaryIndexTab,
|
MaterializedViewTab,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ContainerPolicyTabTypes {
|
export enum ContainerPolicyTabTypes {
|
||||||
@@ -166,15 +165,15 @@ export const getTabTitle = (tab: SettingsV2TabTypes): string => {
|
|||||||
case SettingsV2TabTypes.IndexingPolicyTab:
|
case SettingsV2TabTypes.IndexingPolicyTab:
|
||||||
return "Indexing Policy";
|
return "Indexing Policy";
|
||||||
case SettingsV2TabTypes.PartitionKeyTab:
|
case SettingsV2TabTypes.PartitionKeyTab:
|
||||||
return isFabricNative() ? "Partition Keys" : "Partition Keys (preview)";
|
return "Partition Keys (preview)";
|
||||||
case SettingsV2TabTypes.ComputedPropertiesTab:
|
case SettingsV2TabTypes.ComputedPropertiesTab:
|
||||||
return "Computed Properties";
|
return "Computed Properties";
|
||||||
case SettingsV2TabTypes.ContainerVectorPolicyTab:
|
case SettingsV2TabTypes.ContainerVectorPolicyTab:
|
||||||
return "Container Policies";
|
return "Container Policies";
|
||||||
case SettingsV2TabTypes.ThroughputBucketsTab:
|
case SettingsV2TabTypes.ThroughputBucketsTab:
|
||||||
return "Throughput Buckets";
|
return "Throughput Buckets";
|
||||||
case SettingsV2TabTypes.GlobalSecondaryIndexTab:
|
case SettingsV2TabTypes.MaterializedViewTab:
|
||||||
return "Global Secondary Index (Preview)";
|
return "Materialized Views (Preview)";
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown tab ${tab}`);
|
throw new Error(`Unknown tab ${tab}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,7 @@ export const collection = {
|
|||||||
includedPaths: [],
|
includedPaths: [],
|
||||||
excludedPaths: [],
|
excludedPaths: [],
|
||||||
}),
|
}),
|
||||||
rawDataModel: {
|
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
||||||
uniqueKeyPolicy: {
|
|
||||||
uniqueKeys: [
|
|
||||||
{
|
|
||||||
paths: ["/id"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
usageSizeInKB: ko.observable(100),
|
usageSizeInKB: ko.observable(100),
|
||||||
offer: ko.observable<DataModels.Offer>({
|
offer: ko.observable<DataModels.Offer>({
|
||||||
autoscaleMaxThroughput: undefined,
|
autoscaleMaxThroughput: undefined,
|
||||||
|
|||||||
@@ -71,25 +71,14 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"rawDataModel": {
|
|
||||||
"uniqueKeyPolicy": {
|
|
||||||
"uniqueKeys": [
|
|
||||||
{
|
|
||||||
"paths": [
|
|
||||||
"/id",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isAutoPilotSelected={false}
|
isAutoPilotSelected={false}
|
||||||
isFixedContainer={false}
|
isFixedContainer={false}
|
||||||
isGlobalSecondaryIndex={true}
|
|
||||||
onAutoPilotSelected={[Function]}
|
onAutoPilotSelected={[Function]}
|
||||||
onMaxAutoPilotThroughputChange={[Function]}
|
onMaxAutoPilotThroughputChange={[Function]}
|
||||||
onScaleDiscardableChange={[Function]}
|
onScaleDiscardableChange={[Function]}
|
||||||
@@ -163,18 +152,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"rawDataModel": {
|
|
||||||
"uniqueKeyPolicy": {
|
|
||||||
"uniqueKeys": [
|
|
||||||
{
|
|
||||||
"paths": [
|
|
||||||
"/id",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -198,32 +177,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
timeToLiveSecondsBaseline={5}
|
timeToLiveSecondsBaseline={5}
|
||||||
/>
|
/>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
|
||||||
headerText="Container Policies"
|
|
||||||
itemKey="ContainerVectorPolicyTab"
|
|
||||||
key="ContainerVectorPolicyTab"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginTop": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ContainerPolicyComponent
|
|
||||||
fullTextPolicy={{}}
|
|
||||||
fullTextPolicyBaseline={{}}
|
|
||||||
isFullTextSearchEnabled={true}
|
|
||||||
isGlobalSecondaryIndex={true}
|
|
||||||
isVectorSearchEnabled={false}
|
|
||||||
onFullTextPolicyChange={[Function]}
|
|
||||||
onFullTextPolicyDirtyChange={[Function]}
|
|
||||||
onVectorEmbeddingPolicyChange={[Function]}
|
|
||||||
onVectorEmbeddingPolicyDirtyChange={[Function]}
|
|
||||||
resetShouldDiscardContainerPolicyChange={[Function]}
|
|
||||||
shouldDiscardContainerPolicies={false}
|
|
||||||
vectorEmbeddingPolicy={{}}
|
|
||||||
vectorEmbeddingPolicyBaseline={{}}
|
|
||||||
/>
|
|
||||||
</PivotItem>
|
|
||||||
<PivotItem
|
<PivotItem
|
||||||
headerText="Indexing Policy"
|
headerText="Indexing Policy"
|
||||||
itemKey="IndexingPolicyTab"
|
itemKey="IndexingPolicyTab"
|
||||||
@@ -320,18 +273,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"rawDataModel": {
|
|
||||||
"uniqueKeyPolicy": {
|
|
||||||
"uniqueKeys": [
|
|
||||||
{
|
|
||||||
"paths": [
|
|
||||||
"/id",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -363,7 +306,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isReadOnly={false}
|
|
||||||
/>
|
/>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
@@ -401,16 +343,16 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
/>
|
/>
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem
|
<PivotItem
|
||||||
headerText="Global Secondary Index (Preview)"
|
headerText="Materialized Views (Preview)"
|
||||||
itemKey="GlobalSecondaryIndexTab"
|
itemKey="MaterializedViewTab"
|
||||||
key="GlobalSecondaryIndexTab"
|
key="MaterializedViewTab"
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"marginTop": 20,
|
"marginTop": 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<GlobalSecondaryIndexComponent
|
<MaterializedViewComponent
|
||||||
collection={
|
collection={
|
||||||
{
|
{
|
||||||
"analyticalStorageTtl": [Function],
|
"analyticalStorageTtl": [Function],
|
||||||
@@ -460,18 +402,8 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
"rawDataModel": {
|
|
||||||
"uniqueKeyPolicy": {
|
|
||||||
"uniqueKeys": [
|
|
||||||
{
|
|
||||||
"paths": [
|
|
||||||
"/id",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
|
"uniqueKeyPolicy": {},
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Stack, Text } from "@fluentui/react";
|
import { Text } from "@fluentui/react";
|
||||||
import React, { FunctionComponent } from "react";
|
import React, { FunctionComponent } from "react";
|
||||||
import { InfoTooltip } from "../../../../Common/Tooltip/InfoTooltip";
|
import { InfoTooltip } from "../../../../Common/Tooltip/InfoTooltip";
|
||||||
import * as SharedConstants from "../../../../Shared/Constants";
|
import * as SharedConstants from "../../../../Shared/Constants";
|
||||||
@@ -44,19 +44,13 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
|||||||
const currencySign: string = getCurrencySign(serverId);
|
const currencySign: string = getCurrencySign(serverId);
|
||||||
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
||||||
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
|
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
|
||||||
const estimatedMonthlyCost = "Estimated monthly cost";
|
|
||||||
|
|
||||||
const iconWithEstimatedCostDisclaimer: JSX.Element = (
|
const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>{estimatedCostDisclaimer}</InfoTooltip>;
|
||||||
<InfoTooltip ariaLabelForTooltip={`${estimatedMonthlyCost} ${currency} ${estimatedCostDisclaimer}`}>
|
|
||||||
{estimatedCostDisclaimer}
|
|
||||||
</InfoTooltip>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isAutoscale) {
|
if (isAutoscale) {
|
||||||
return (
|
return (
|
||||||
<Stack style={{ marginBottom: 6 }}>
|
|
||||||
<Text variant="small">
|
<Text variant="small">
|
||||||
{estimatedMonthlyCost} ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
Estimated monthly cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||||
<b>
|
<b>
|
||||||
{currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "}
|
{currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "}
|
||||||
{currencySign + calculateEstimateNumber(monthlyPrice)}{" "}
|
{currencySign + calculateEstimateNumber(monthlyPrice)}{" "}
|
||||||
@@ -64,12 +58,10 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
|||||||
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits / 10} - {requestUnits}{" "}
|
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits / 10} - {requestUnits}{" "}
|
||||||
RU/s, {currencySign + pricePerRu}/RU)
|
RU/s, {currencySign + pricePerRu}/RU)
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack style={{ marginBottom: 8 }}>
|
|
||||||
<Text variant="small">
|
<Text variant="small">
|
||||||
Estimated cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
Estimated cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||||
<b>
|
<b>
|
||||||
@@ -80,6 +72,5 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
|||||||
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits}RU/s,{" "}
|
({numberOfRegions + (numberOfRegions === 1 ? " region" : " regions")}, {requestUnits}RU/s,{" "}
|
||||||
{currencySign + pricePerRu}/RU)
|
{currencySign + pricePerRu}/RU)
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { Checkbox, DirectionalHint, Link, Separator, Stack, Text, TextField, TooltipHost } from "@fluentui/react";
|
import { Checkbox, DirectionalHint, Link, Stack, Text, TextField, TooltipHost } from "@fluentui/react";
|
||||||
import { getWorkloadType } from "Common/DatabaseAccountUtility";
|
import { getWorkloadType } from "Common/DatabaseAccountUtility";
|
||||||
import { CostEstimateText } from "Explorer/Controls/ThroughputInput/CostEstimateText/CostEstimateText";
|
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
|
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
|
||||||
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
|
|
||||||
import * as SharedConstants from "../../../Shared/Constants";
|
import * as SharedConstants from "../../../Shared/Constants";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import { getCollectionName } from "../../../Utils/APITypeUtils";
|
import { getCollectionName } from "../../../Utils/APITypeUtils";
|
||||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||||
import * as PricingUtils from "../../../Utils/PricingUtils";
|
import * as PricingUtils from "../../../Utils/PricingUtils";
|
||||||
|
import { CostEstimateText } from "./CostEstimateText/CostEstimateText";
|
||||||
import "./ThroughputInput.less";
|
import "./ThroughputInput.less";
|
||||||
|
|
||||||
export interface ThroughputInputProps {
|
export interface ThroughputInputProps {
|
||||||
@@ -19,7 +18,6 @@ export interface ThroughputInputProps {
|
|||||||
isFreeTier: boolean;
|
isFreeTier: boolean;
|
||||||
showFreeTierExceedThroughputTooltip: boolean;
|
showFreeTierExceedThroughputTooltip: boolean;
|
||||||
isQuickstart?: boolean;
|
isQuickstart?: boolean;
|
||||||
isGlobalSecondaryIndex?: boolean;
|
|
||||||
setThroughputValue: (throughput: number) => void;
|
setThroughputValue: (throughput: number) => void;
|
||||||
setIsAutoscale: (isAutoscale: boolean) => void;
|
setIsAutoscale: (isAutoscale: boolean) => void;
|
||||||
setIsThroughputCapExceeded: (isThroughputCapExceeded: boolean) => void;
|
setIsThroughputCapExceeded: (isThroughputCapExceeded: boolean) => void;
|
||||||
@@ -32,7 +30,6 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
isFreeTier,
|
isFreeTier,
|
||||||
showFreeTierExceedThroughputTooltip,
|
showFreeTierExceedThroughputTooltip,
|
||||||
isQuickstart,
|
isQuickstart,
|
||||||
isGlobalSecondaryIndex,
|
|
||||||
setThroughputValue,
|
setThroughputValue,
|
||||||
setIsAutoscale,
|
setIsAutoscale,
|
||||||
setIsThroughputCapExceeded,
|
setIsThroughputCapExceeded,
|
||||||
@@ -41,9 +38,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
let defaultThroughput: number;
|
let defaultThroughput: number;
|
||||||
const workloadType: Constants.WorkloadType = getWorkloadType();
|
const workloadType: Constants.WorkloadType = getWorkloadType();
|
||||||
|
|
||||||
if (isFabricNative()) {
|
if (
|
||||||
defaultThroughput = AutoPilotUtils.autoPilotThroughput5K;
|
|
||||||
} else if (
|
|
||||||
isFreeTier ||
|
isFreeTier ||
|
||||||
isQuickstart ||
|
isQuickstart ||
|
||||||
[Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(workloadType)
|
[Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(workloadType)
|
||||||
@@ -198,7 +193,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<InfoTooltip>{PricingUtils.getRuToolTipText()}</InfoTooltip>
|
<InfoTooltip>{PricingUtils.getRuToolTipText()}</InfoTooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
{!isGlobalSecondaryIndex && (
|
|
||||||
<Stack horizontal verticalAlign="center">
|
<Stack horizontal verticalAlign="center">
|
||||||
<div role="radiogroup">
|
<div role="radiogroup">
|
||||||
<input
|
<input
|
||||||
@@ -232,66 +227,37 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
|
||||||
|
|
||||||
{isAutoscaleSelected && (
|
{isAutoscaleSelected && (
|
||||||
<Stack className="throughputInputSpacing">
|
<Stack className="throughputInputSpacing">
|
||||||
<Text style={{ marginTop: -2, fontSize: 12 }}>
|
<Text variant="small" aria-label="capacity calculator of azure cosmos db">
|
||||||
Your container throughput will automatically scale up to the maximum value you select, from a minimum of 10%
|
Estimate your required RU/s with{" "}
|
||||||
of that value.
|
<Link
|
||||||
</Text>
|
className="underlinedLink outlineNone"
|
||||||
<Stack horizontal verticalAlign="end" tokens={{ childrenGap: 8 }}>
|
target="_blank"
|
||||||
<Stack tokens={{ childrenGap: 4 }}>
|
href="https://cosmos.azure.com/capacitycalculator/"
|
||||||
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}>
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
|
||||||
Minimum RU/s
|
|
||||||
</Text>
|
|
||||||
<InfoTooltip>The minimum RU/s your container will scale to</InfoTooltip>
|
|
||||||
</Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "Segoe UI",
|
|
||||||
width: 70,
|
|
||||||
height: 27,
|
|
||||||
border: "none",
|
|
||||||
fontSize: 14,
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
fontWeight: 400,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{Math.round(throughput / 10).toString()}
|
capacity calculator
|
||||||
</Text>
|
</Link>
|
||||||
</Stack>
|
.
|
||||||
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontFamily: "Segoe UI",
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: 400,
|
|
||||||
paddingBottom: 6,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
x 10 =
|
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Stack tokens={{ childrenGap: 4 }}>
|
<Stack horizontal>
|
||||||
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 4 }}>
|
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }} aria-label="maxRUDescription">
|
||||||
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }}>
|
{isDatabase ? "Database" : getCollectionName()} Max RU/s
|
||||||
Maximum RU/s
|
|
||||||
</Text>
|
</Text>
|
||||||
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
|
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
id="autoscaleRUValueField"
|
id="autoscaleRUValueField"
|
||||||
type="number"
|
type="number"
|
||||||
styles={{
|
styles={{
|
||||||
fieldGroup: { width: 100, height: 27, flexShrink: 0 },
|
fieldGroup: { width: 300, height: 27 },
|
||||||
field: { fontSize: 14, fontWeight: 400 },
|
field: { fontSize: 12 },
|
||||||
}}
|
}}
|
||||||
onChange={(_event, newInput?: string) => onThroughputValueChange(newInput)}
|
onChange={(event, newInput?: string) => onThroughputValueChange(newInput)}
|
||||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||||
min={AutoPilotUtils.autoPilotThroughput1K}
|
min={AutoPilotUtils.autoPilotThroughput1K}
|
||||||
max={isSharded ? Number.MAX_SAFE_INTEGER.toString() : "10000"}
|
max={isSharded ? Number.MAX_SAFE_INTEGER.toString() : "10000"}
|
||||||
@@ -300,26 +266,16 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
required={true}
|
required={true}
|
||||||
errorMessage={throughputError}
|
errorMessage={throughputError}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
|
<Text variant="small">
|
||||||
<Stack className="throughputInputSpacing">
|
Your {isDatabase ? "database" : getCollectionName().toLocaleLowerCase()} throughput will automatically scale
|
||||||
<Text variant="small" aria-label="ruDescription">
|
from{" "}
|
||||||
Estimate your required RU/s with
|
<b>
|
||||||
<Link
|
{AutoPilotUtils.getMinRUsBasedOnUserInput(throughput)} RU/s (10% of max RU/s) - {throughput} RU/s
|
||||||
className="underlinedLink"
|
</b>{" "}
|
||||||
target="_blank"
|
based on usage.
|
||||||
href="https://cosmos.azure.com/capacitycalculator/"
|
|
||||||
aria-label="Capacity calculator"
|
|
||||||
>
|
|
||||||
capacity calculator
|
|
||||||
</Link>
|
|
||||||
.
|
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Separator className="panelSeparator" style={{ paddingTop: -8, paddingBottom: -8 }} />
|
|
||||||
</Stack>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isAutoscaleSelected && (
|
{!isAutoscaleSelected && (
|
||||||
@@ -342,6 +298,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
|
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<TooltipHost
|
<TooltipHost
|
||||||
directionalHint={DirectionalHint.topLeftEdge}
|
directionalHint={DirectionalHint.topLeftEdge}
|
||||||
content={
|
content={
|
||||||
@@ -366,10 +323,11 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
errorMessage={throughputError}
|
errorMessage={throughputError}
|
||||||
/>
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<CostEstimateText requestUnits={throughput} isAutoscale={isAutoscaleSelected} />
|
||||||
|
|
||||||
{throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && (
|
{throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && (
|
||||||
<Stack horizontal verticalAlign="start">
|
<Stack horizontal verticalAlign="start">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
|
||||||
import { VectorEmbedding, VectorIndex } from "Contracts/DataModels";
|
import { VectorEmbedding, VectorIndex } from "Contracts/DataModels";
|
||||||
import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
import {
|
import {
|
||||||
@@ -30,7 +29,6 @@ export interface IVectorEmbeddingPoliciesComponentProps {
|
|||||||
discardChanges?: boolean;
|
discardChanges?: boolean;
|
||||||
onChangesDiscarded?: () => void;
|
onChangesDiscarded?: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
isGlobalSecondaryIndex?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VectorEmbeddingPolicyData {
|
export interface VectorEmbeddingPolicyData {
|
||||||
@@ -41,7 +39,8 @@ export interface VectorEmbeddingPolicyData {
|
|||||||
indexType: VectorIndex["type"] | "none";
|
indexType: VectorIndex["type"] | "none";
|
||||||
pathError: string;
|
pathError: string;
|
||||||
dimensionsError: string;
|
dimensionsError: string;
|
||||||
vectorIndexShardKey?: string[];
|
diskANNShardKey?: string;
|
||||||
|
diskANNShardKeyError?: string;
|
||||||
indexingSearchListSize?: number;
|
indexingSearchListSize?: number;
|
||||||
indexingSearchListSizeError?: string;
|
indexingSearchListSizeError?: string;
|
||||||
quantizationByteSize?: number;
|
quantizationByteSize?: number;
|
||||||
@@ -88,7 +87,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
discardChanges,
|
discardChanges,
|
||||||
onChangesDiscarded,
|
onChangesDiscarded,
|
||||||
disabled,
|
disabled,
|
||||||
isGlobalSecondaryIndex,
|
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const onVectorEmbeddingPathError = (path: string, index?: number): string => {
|
const onVectorEmbeddingPathError = (path: string, index?: number): string => {
|
||||||
let error = "";
|
let error = "";
|
||||||
@@ -134,6 +132,12 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
return error;
|
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 initializeData = (vectorEmbeddings: VectorEmbedding[], vectorIndexes: VectorIndex[]) => {
|
||||||
const mergedData: VectorEmbeddingPolicyData[] = [];
|
const mergedData: VectorEmbeddingPolicyData[] = [];
|
||||||
vectorEmbeddings.forEach((embedding) => {
|
vectorEmbeddings.forEach((embedding) => {
|
||||||
@@ -143,7 +147,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
indexType: matchingIndex?.type || "none",
|
indexType: matchingIndex?.type || "none",
|
||||||
indexingSearchListSize: matchingIndex?.indexingSearchListSize || undefined,
|
indexingSearchListSize: matchingIndex?.indexingSearchListSize || undefined,
|
||||||
quantizationByteSize: matchingIndex?.quantizationByteSize || undefined,
|
quantizationByteSize: matchingIndex?.quantizationByteSize || undefined,
|
||||||
vectorIndexShardKey: matchingIndex?.vectorIndexShardKey || undefined,
|
|
||||||
pathError: onVectorEmbeddingPathError(embedding.path),
|
pathError: onVectorEmbeddingPathError(embedding.path),
|
||||||
dimensionsError: onVectorEmbeddingDimensionError(embedding.dimensions, matchingIndex?.type || "none"),
|
dimensionsError: onVectorEmbeddingDimensionError(embedding.dimensions, matchingIndex?.type || "none"),
|
||||||
});
|
});
|
||||||
@@ -183,7 +186,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
type: policy.indexType,
|
type: policy.indexType,
|
||||||
indexingSearchListSize: policy.indexingSearchListSize,
|
indexingSearchListSize: policy.indexingSearchListSize,
|
||||||
quantizationByteSize: policy.quantizationByteSize,
|
quantizationByteSize: policy.quantizationByteSize,
|
||||||
vectorIndexShardKey: policy.vectorIndexShardKey,
|
|
||||||
}) as VectorIndex,
|
}) as VectorIndex,
|
||||||
);
|
);
|
||||||
const validationPassed = vectorEmbeddingPolicyData.every(
|
const validationPassed = vectorEmbeddingPolicyData.every(
|
||||||
@@ -245,16 +247,20 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
setVectorEmbeddingPolicyData(vectorEmbeddings);
|
setVectorEmbeddingPolicyData(vectorEmbeddings);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onShardKeyChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => {
|
// TODO: uncomment after Ignite
|
||||||
const value = event.target.value.trim();
|
// DiskANNShardKey was removed for Ignite due to backend problems. Leaving this here as it will be reinstated immediately after Ignite
|
||||||
const vectorEmbeddings = [...vectorEmbeddingPolicyData];
|
// const onDiskANNShardKeyChange = (index: number, event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (!vectorEmbeddings[index]?.vectorIndexShardKey?.[0] && !value.startsWith("/")) {
|
// const value = event.target.value.trim();
|
||||||
vectorEmbeddings[index].vectorIndexShardKey = ["/" + value];
|
// const vectorEmbeddings = [...vectorEmbeddingPolicyData];
|
||||||
} else {
|
// if (!vectorEmbeddings[index]?.diskANNShardKey && !value.startsWith("/")) {
|
||||||
vectorEmbeddings[index].vectorIndexShardKey = [value];
|
// vectorEmbeddings[index].diskANNShardKey = "/" + value;
|
||||||
}
|
// } else {
|
||||||
setVectorEmbeddingPolicyData(vectorEmbeddings);
|
// vectorEmbeddings[index].diskANNShardKey = value;
|
||||||
};
|
// }
|
||||||
|
// const error = onDiskANNShardKeyError(value);
|
||||||
|
// vectorEmbeddings[index].diskANNShardKeyError = error;
|
||||||
|
// setVectorEmbeddingPolicyData(vectorEmbeddings);
|
||||||
|
// }
|
||||||
|
|
||||||
const onVectorEmbeddingPolicyChange = (
|
const onVectorEmbeddingPolicyChange = (
|
||||||
index: number,
|
index: number,
|
||||||
@@ -286,11 +292,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
setVectorEmbeddingPolicyData(vectorEmbeddings);
|
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 (
|
return (
|
||||||
<Stack tokens={{ childrenGap: 4 }}>
|
<Stack tokens={{ childrenGap: 4 }}>
|
||||||
{vectorEmbeddingPolicyData &&
|
{vectorEmbeddingPolicyData &&
|
||||||
@@ -401,7 +402,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
styles={labelStyles}
|
styles={labelStyles}
|
||||||
>
|
>
|
||||||
Quantization byte size
|
Quantization byte size
|
||||||
<InfoTooltip>{getQuantizationByteSizeTooltipContent()}</InfoTooltip>
|
|
||||||
</Label>
|
</Label>
|
||||||
<TextField
|
<TextField
|
||||||
disabled={
|
disabled={
|
||||||
@@ -431,18 +431,26 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack style={{ marginLeft: "10px" }}>
|
{/*TODO: uncomment after Ignite */}
|
||||||
<Label disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"} styles={labelStyles}>
|
{/* DiskANNShardKey was removed for Ignite due to backend problems. Leaving this here as it will be reinstated immediately after Ignite
|
||||||
Vector index shard key
|
<Stack
|
||||||
</Label>
|
style={{ marginLeft: "10px" }}
|
||||||
|
>
|
||||||
|
<Label
|
||||||
|
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
|
||||||
|
styles={labelStyles}
|
||||||
|
>DiskANN shard key</Label>
|
||||||
<TextField
|
<TextField
|
||||||
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
|
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
|
||||||
id={`vector-policy-vectorIndexShardKey-${index + 1}`}
|
id={`vector-policy-diskANNShardKey-${index + 1}`}
|
||||||
styles={textFieldStyles}
|
styles={textFieldStyles}
|
||||||
value={String(vectorEmbeddingPolicy.vectorIndexShardKey?.[0] ?? "")}
|
value={String(vectorEmbeddingPolicy.diskANNShardKey || "")}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => onShardKeyChange(index, event)}
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onDiskANNShardKeyChange(index, event)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
*/}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useDataplaneRbacAuthorization } from "Utils/AuthorizationUtils";
|
|
||||||
import { createCollection } from "../../Common/dataAccess/createCollection";
|
import { createCollection } from "../../Common/dataAccess/createCollection";
|
||||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
import { createDocument as createMongoDocument } from "../../Common/MongoProxyClient";
|
import { createDocument as createMongoDocument } from "../../Common/MongoProxyClient";
|
||||||
@@ -91,13 +90,12 @@ export class ContainerSampleGenerator {
|
|||||||
}
|
}
|
||||||
const { databaseAccount: account } = userContext;
|
const { databaseAccount: account } = userContext;
|
||||||
const databaseId = collection.databaseId;
|
const databaseId = collection.databaseId;
|
||||||
|
|
||||||
const gremlinClient = new GremlinClient();
|
const gremlinClient = new GremlinClient();
|
||||||
gremlinClient.initialize({
|
gremlinClient.initialize({
|
||||||
endpoint: `wss://${GraphTab.getGremlinEndpoint(account)}`,
|
endpoint: `wss://${GraphTab.getGremlinEndpoint(account)}`,
|
||||||
databaseId: databaseId,
|
databaseId: databaseId,
|
||||||
collectionId: collection.id(),
|
collectionId: collection.id(),
|
||||||
password: useDataplaneRbacAuthorization(userContext) ? userContext.aadToken : userContext.masterKey || "",
|
masterKey: userContext.masterKey || "",
|
||||||
maxResultSize: 100,
|
maxResultSize: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import Explorer from "../Explorer";
|
|||||||
import { useDatabases } from "../useDatabases";
|
import { useDatabases } from "../useDatabases";
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
|
|
||||||
// TODO: this does not seem to be used. Remove?
|
|
||||||
export class DataSamplesUtil {
|
export class DataSamplesUtil {
|
||||||
private static readonly DialogTitle = "Create Sample Container";
|
private static readonly DialogTitle = "Create Sample Container";
|
||||||
constructor(private container: Explorer) {}
|
constructor(private container: Explorer) {}
|
||||||
|
|||||||
@@ -8,16 +8,10 @@ import { MessageTypes } from "Contracts/ExplorerContracts";
|
|||||||
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
|
||||||
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||||
import { IGalleryItem } from "Juno/JunoClient";
|
import { IGalleryItem } from "Juno/JunoClient";
|
||||||
import {
|
import { isFabricMirrored, isFabricMirroredKey, scheduleRefreshFabricToken } from "Platform/Fabric/FabricUtil";
|
||||||
isFabricMirrored,
|
|
||||||
isFabricMirroredKey,
|
|
||||||
isFabricNative,
|
|
||||||
scheduleRefreshFabricToken,
|
|
||||||
} from "Platform/Fabric/FabricUtil";
|
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
||||||
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
|
|
||||||
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
@@ -36,7 +30,6 @@ import { readDatabases } from "../Common/dataAccess/readDatabases";
|
|||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse } from "../Contracts/DataModels";
|
import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse } from "../Contracts/DataModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { UploadDetailsRecord } from "../Contracts/ViewModels";
|
|
||||||
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
|
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
|
||||||
import { PhoenixClient } from "../Phoenix/PhoenixClient";
|
import { PhoenixClient } from "../Phoenix/PhoenixClient";
|
||||||
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
||||||
@@ -289,68 +282,6 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a VS Code DocumentDB connection URL using the current user's MongoDB connection parameters.
|
|
||||||
* Double-encodes the updated connection string for safe usage in VS Code URLs.
|
|
||||||
*
|
|
||||||
* The DocumentDB VS Code extension requires double encoding for connection strings.
|
|
||||||
* See: https://microsoft.github.io/vscode-documentdb/manual/how-to-construct-url.html#double-encoding
|
|
||||||
*
|
|
||||||
* @returns {string} The encoded VS Code DocumentDB connection URL.
|
|
||||||
*/
|
|
||||||
private getDocumentDbUrl() {
|
|
||||||
const { adminLogin: adminLoginuserName = "", connectionString = "" } = userContext.vcoreMongoConnectionParams;
|
|
||||||
const updatedConnectionString = connectionString.replace(/<(user|username)>:<password>/i, adminLoginuserName);
|
|
||||||
const encodedUpdatedConnectionString = encodeURIComponent(encodeURIComponent(updatedConnectionString));
|
|
||||||
const documentDbUrl = `vscode://ms-azuretools.vscode-documentdb?connectionString=${encodedUpdatedConnectionString}`;
|
|
||||||
return documentDbUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCosmosDbUrl() {
|
|
||||||
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;
|
|
||||||
return vscodeUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getVSCodeUrl(): string {
|
|
||||||
const isvCore = (userContext.apiType || userContext.databaseAccount.kind) === "VCoreMongo";
|
|
||||||
return isvCore ? this.getDocumentDbUrl() : this.getCosmosDbUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public openInVsCode(): void {
|
|
||||||
const vscodeUrl = this.getVSCodeUrl();
|
|
||||||
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> {
|
public async openCESCVAFeedbackBlade(): Promise<void> {
|
||||||
sendMessage({ type: MessageTypes.OpenCESCVAFeedbackBlade });
|
sendMessage({ type: MessageTypes.OpenCESCVAFeedbackBlade });
|
||||||
Logger.logInfo(
|
Logger.logInfo(
|
||||||
@@ -979,9 +910,7 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async openNotebookTerminal(kind: ViewModels.TerminalKind): Promise<void> {
|
public async openNotebookTerminal(kind: ViewModels.TerminalKind): Promise<void> {
|
||||||
if (userContext.features.enableCloudShell) {
|
if (useNotebook.getState().isPhoenixFeatures) {
|
||||||
this.connectToNotebookTerminal(kind);
|
|
||||||
} else if (useNotebook.getState().isPhoenixFeatures) {
|
|
||||||
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
await this.allocateContainer(PoolIdType.DefaultPoolId);
|
||||||
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||||
if (notebookServerInfo && notebookServerInfo.notebookServerEndpoint !== undefined) {
|
if (notebookServerInfo && notebookServerInfo.notebookServerEndpoint !== undefined) {
|
||||||
@@ -1147,8 +1076,8 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public openUploadItemsPane(onUpload?: (data: UploadDetailsRecord[]) => void): void {
|
public openUploadItemsPane(): void {
|
||||||
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane onUpload={onUpload} />);
|
useSidePanel.getState().openSidePanel("Upload " + getUploadName(), <UploadItemsPane />);
|
||||||
}
|
}
|
||||||
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
|
public openExecuteSprocParamsPanel(storedProcedure: StoredProcedure): void {
|
||||||
useSidePanel
|
useSidePanel
|
||||||
@@ -1156,7 +1085,7 @@ export default class Explorer {
|
|||||||
.openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />);
|
.openSidePanel("Input parameters", <ExecuteSprocParamsPane storedProcedure={storedProcedure} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDownloadModalContent(fileName: string): JSX.Element {
|
public getDownloadModalConent(fileName: string): JSX.Element {
|
||||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -1180,10 +1109,7 @@ export default class Explorer {
|
|||||||
? this.refreshDatabaseForResourceToken()
|
? this.refreshDatabaseForResourceToken()
|
||||||
: await this.refreshAllDatabases(); // await: we rely on the databases to be loaded before restoring the tabs further in the flow
|
: await this.refreshAllDatabases(); // await: we rely on the databases to be loaded before restoring the tabs further in the flow
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isFabricNative()) {
|
|
||||||
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
||||||
const isNotebookEnabled =
|
const isNotebookEnabled =
|
||||||
@@ -1205,11 +1131,6 @@ export default class Explorer {
|
|||||||
await this.initNotebooks(userContext.databaseAccount);
|
await this.initNotebooks(userContext.databaseAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.authType === AuthType.AAD && userContext.apiType === "SQL" && !isFabricNative()) {
|
|
||||||
const throughputBucketsEnabled = await featureRegistered(userContext.subscriptionId, "ThroughputBucketing");
|
|
||||||
updateUserContext({ throughputBucketsEnabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshSampleData();
|
this.refreshSampleData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,8 @@ describe("GraphExplorer", () => {
|
|||||||
graphBackendEndpoint: "graphBackendEndpoint",
|
graphBackendEndpoint: "graphBackendEndpoint",
|
||||||
databaseId: "databaseId",
|
databaseId: "databaseId",
|
||||||
collectionId: "collectionId",
|
collectionId: "collectionId",
|
||||||
password: "password",
|
masterKey: "masterKey",
|
||||||
|
|
||||||
onLoadStartKey: 0,
|
onLoadStartKey: 0,
|
||||||
onLoadStartKeyChange: (newKey: number): void => {},
|
onLoadStartKeyChange: (newKey: number): void => {},
|
||||||
resourceId: "resourceId",
|
resourceId: "resourceId",
|
||||||
|
|||||||
@@ -16,12 +16,7 @@ import * as StorageUtility from "../../../Shared/StorageUtility";
|
|||||||
import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
|
||||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import {
|
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||||
logConsoleError,
|
|
||||||
logConsoleInfo,
|
|
||||||
logConsoleProgress,
|
|
||||||
logConsoleWarning,
|
|
||||||
} from "../../../Utils/NotificationConsoleUtils";
|
|
||||||
import { EditorReact } from "../../Controls/Editor/EditorReact";
|
import { EditorReact } from "../../Controls/Editor/EditorReact";
|
||||||
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
|
import * as InputTypeaheadComponent from "../../Controls/InputTypeahead/InputTypeaheadComponent";
|
||||||
import * as TabComponent from "../../Controls/Tabs/TabComponent";
|
import * as TabComponent from "../../Controls/Tabs/TabComponent";
|
||||||
@@ -59,7 +54,7 @@ export interface GraphExplorerProps {
|
|||||||
graphBackendEndpoint: string;
|
graphBackendEndpoint: string;
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
password: string;
|
masterKey: string;
|
||||||
|
|
||||||
onLoadStartKey: number;
|
onLoadStartKey: number;
|
||||||
onLoadStartKeyChange: (newKey: number) => void;
|
onLoadStartKeyChange: (newKey: number) => void;
|
||||||
@@ -1088,7 +1083,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
public static reportToConsole(type: ConsoleDataType.InProgress, msg: string, ...errorData: any[]): () => void;
|
public static reportToConsole(type: ConsoleDataType.InProgress, msg: string, ...errorData: any[]): () => void;
|
||||||
public static reportToConsole(type: ConsoleDataType.Info, msg: string, ...errorData: any[]): void;
|
public static reportToConsole(type: ConsoleDataType.Info, msg: string, ...errorData: any[]): void;
|
||||||
public static reportToConsole(type: ConsoleDataType.Error, msg: string, ...errorData: any[]): void;
|
public static reportToConsole(type: ConsoleDataType.Error, msg: string, ...errorData: any[]): void;
|
||||||
public static reportToConsole(type: ConsoleDataType.Warning, msg: string, ...errorData: any[]): void;
|
|
||||||
public static reportToConsole(type: ConsoleDataType, msg: string, ...errorData: any[]): void | (() => void) {
|
public static reportToConsole(type: ConsoleDataType, msg: string, ...errorData: any[]): void | (() => void) {
|
||||||
let errorDataStr = "";
|
let errorDataStr = "";
|
||||||
if (errorData && errorData.length > 0) {
|
if (errorData && errorData.length > 0) {
|
||||||
@@ -1105,8 +1099,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
return logConsoleInfo(consoleMessage);
|
return logConsoleInfo(consoleMessage);
|
||||||
case ConsoleDataType.InProgress:
|
case ConsoleDataType.InProgress:
|
||||||
return logConsoleProgress(consoleMessage);
|
return logConsoleProgress(consoleMessage);
|
||||||
case ConsoleDataType.Warning:
|
|
||||||
return logConsoleWarning(consoleMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1300,7 +1292,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
endpoint: `wss://${this.props.graphBackendEndpoint}`,
|
endpoint: `wss://${this.props.graphBackendEndpoint}`,
|
||||||
databaseId: this.props.databaseId,
|
databaseId: this.props.databaseId,
|
||||||
collectionId: this.props.collectionId,
|
collectionId: this.props.collectionId,
|
||||||
password: this.props.password,
|
masterKey: this.props.masterKey,
|
||||||
maxResultSize: GraphExplorer.MAX_RESULT_SIZE,
|
maxResultSize: GraphExplorer.MAX_RESULT_SIZE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,28 +8,28 @@ describe("Gremlin Client", () => {
|
|||||||
endpoint: null,
|
endpoint: null,
|
||||||
collectionId: null,
|
collectionId: null,
|
||||||
databaseId: null,
|
databaseId: null,
|
||||||
|
masterKey: null,
|
||||||
maxResultSize: 10000,
|
maxResultSize: 10000,
|
||||||
password: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("should use databaseId, collectionId and password to authenticate", () => {
|
it("should use databaseId, collectionId and masterKey to authenticate", () => {
|
||||||
const collectionId = "collectionId";
|
const collectionId = "collectionId";
|
||||||
const databaseId = "databaseId";
|
const databaseId = "databaseId";
|
||||||
const testPassword = "password";
|
const masterKey = "masterKey";
|
||||||
const gremlinClient = new GremlinClient();
|
const gremlinClient = new GremlinClient();
|
||||||
|
|
||||||
gremlinClient.initialize({
|
gremlinClient.initialize({
|
||||||
endpoint: null,
|
endpoint: null,
|
||||||
collectionId,
|
collectionId,
|
||||||
databaseId,
|
databaseId,
|
||||||
|
masterKey,
|
||||||
maxResultSize: 0,
|
maxResultSize: 0,
|
||||||
password: testPassword,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// User must includes these values
|
// User must includes these values
|
||||||
expect(gremlinClient.client.params.user.indexOf(collectionId)).not.toBe(-1);
|
expect(gremlinClient.client.params.user.indexOf(collectionId)).not.toBe(-1);
|
||||||
expect(gremlinClient.client.params.user.indexOf(databaseId)).not.toBe(-1);
|
expect(gremlinClient.client.params.user.indexOf(databaseId)).not.toBe(-1);
|
||||||
expect(gremlinClient.client.params.password).toEqual(testPassword);
|
expect(gremlinClient.client.params.password).toEqual(masterKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should aggregate RU charges across multiple responses", (done) => {
|
it("should aggregate RU charges across multiple responses", (done) => {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ export interface GremlinClientParameters {
|
|||||||
endpoint: string;
|
endpoint: string;
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
|
masterKey: string;
|
||||||
maxResultSize: number;
|
maxResultSize: number;
|
||||||
password: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GremlinRequestResult {
|
export interface GremlinRequestResult {
|
||||||
@@ -43,7 +43,7 @@ export class GremlinClient {
|
|||||||
this.client = new GremlinSimpleClient({
|
this.client = new GremlinSimpleClient({
|
||||||
endpoint: params.endpoint,
|
endpoint: params.endpoint,
|
||||||
user: `/dbs/${params.databaseId}/colls/${params.collectionId}`,
|
user: `/dbs/${params.databaseId}/colls/${params.collectionId}`,
|
||||||
password: params.password,
|
password: params.masterKey,
|
||||||
successCallback: (result: Result) => {
|
successCallback: (result: Result) => {
|
||||||
this.storePendingResult(result);
|
this.storePendingResult(result);
|
||||||
this.flushResult(result.requestId);
|
this.flushResult(result.requestId);
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import {
|
import {
|
||||||
GremlinRequestMessage,
|
|
||||||
GremlinResponseMessage,
|
|
||||||
GremlinSimpleClient,
|
GremlinSimpleClient,
|
||||||
GremlinSimpleClientParameters,
|
GremlinSimpleClientParameters,
|
||||||
Result,
|
Result,
|
||||||
|
GremlinRequestMessage,
|
||||||
|
GremlinResponseMessage,
|
||||||
} from "./GremlinSimpleClient";
|
} from "./GremlinSimpleClient";
|
||||||
|
|
||||||
describe("Gremlin Simple Client", () => {
|
describe("Gremlin Simple Client", () => {
|
||||||
|
|||||||
@@ -95,10 +95,3 @@
|
|||||||
white-space: nowrap;
|
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 OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
||||||
import SettingsIcon from "../../../../images/settings_15x15.svg";
|
import SettingsIcon from "../../../../images/settings_15x15.svg";
|
||||||
import SynapseIcon from "../../../../images/synapse-link.svg";
|
import SynapseIcon from "../../../../images/synapse-link.svg";
|
||||||
import VSCodeIcon from "../../../../images/vscode.svg";
|
|
||||||
import { AuthType } from "../../../AuthType";
|
import { AuthType } from "../../../AuthType";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { Platform, configContext } from "../../../ConfigContext";
|
import { Platform, configContext } from "../../../ConfigContext";
|
||||||
@@ -61,10 +60,6 @@ export function createStaticCommandBarButtons(
|
|||||||
addDivider();
|
addDivider();
|
||||||
buttons.push(addSynapseLink);
|
buttons.push(addSynapseLink);
|
||||||
}
|
}
|
||||||
if (userContext.apiType !== "Gremlin") {
|
|
||||||
const addVsCode = createOpenVsCodeDialogButton(container);
|
|
||||||
buttons.push(addVsCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDataplaneRbacSupported(userContext.apiType)) {
|
if (isDataplaneRbacSupported(userContext.apiType)) {
|
||||||
@@ -131,14 +126,13 @@ export function createContextCommandBarButtons(
|
|||||||
const buttons: CommandButtonComponentProps[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
if (!selectedNodeState.isDatabaseNodeOrNoneSelected() && userContext.apiType === "Mongo") {
|
||||||
const label =
|
const label = useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell";
|
||||||
useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell ? "Open Mongo Shell" : "New Shell";
|
|
||||||
const newMongoShellBtn: CommandButtonComponentProps = {
|
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||||
if (useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell) {
|
if (useNotebook.getState().isShellEnabled) {
|
||||||
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
|
||||||
} else {
|
} else {
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
@@ -152,7 +146,7 @@ export function createContextCommandBarButtons(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell) &&
|
useNotebook.getState().isShellEnabled &&
|
||||||
!selectedNodeState.isDatabaseNodeOrNoneSelected() &&
|
!selectedNodeState.isDatabaseNodeOrNoneSelected() &&
|
||||||
userContext.apiType === "Cassandra"
|
userContext.apiType === "Cassandra"
|
||||||
) {
|
) {
|
||||||
@@ -273,18 +267,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 {
|
function createLoginForEntraIDButton(container: Explorer): CommandButtonComponentProps {
|
||||||
if (configContext.platform !== Platform.Portal) {
|
if (configContext.platform !== Platform.Portal) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -473,7 +455,7 @@ function createOpenTerminalButtonByKind(
|
|||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
if (useNotebook.getState().isNotebookEnabled || userContext.features.enableCloudShell) {
|
if (useNotebook.getState().isNotebookEnabled) {
|
||||||
container.openNotebookTerminal(terminalKind);
|
container.openNotebookTerminal(terminalKind);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -517,6 +499,6 @@ export function createPostgreButtons(container: Explorer): CommandButtonComponen
|
|||||||
|
|
||||||
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
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,
|
Info = 0,
|
||||||
Error = 1,
|
Error = 1,
|
||||||
InProgress = 2,
|
InProgress = 2,
|
||||||
Warning = 3,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,20 +173,8 @@
|
|||||||
.message {
|
.message {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
white-space:pre-wrap;
|
white-space:pre-wrap;
|
||||||
overflow-wrap: break-word;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.notificationConsoleContents {
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.notificationConsoleData {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,6 @@ import ErrorRedIcon from "../../../../images/error_red.svg";
|
|||||||
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
||||||
import InfoIcon from "../../../../images/info_color.svg";
|
import InfoIcon from "../../../../images/info_color.svg";
|
||||||
import LoadingIcon from "../../../../images/loading.svg";
|
import LoadingIcon from "../../../../images/loading.svg";
|
||||||
import WarningIcon from "../../../../images/warning.svg";
|
|
||||||
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
|
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
||||||
@@ -92,9 +91,6 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
const numInfoItems = this.state.allConsoleData.filter(
|
const numInfoItems = this.state.allConsoleData.filter(
|
||||||
(data: ConsoleData) => data.type === ConsoleDataType.Info,
|
(data: ConsoleData) => data.type === ConsoleDataType.Info,
|
||||||
).length;
|
).length;
|
||||||
const numWarningItems = this.state.allConsoleData.filter(
|
|
||||||
(data: ConsoleData) => data.type === ConsoleDataType.Warning,
|
|
||||||
).length;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="notificationConsoleContainer">
|
<div className="notificationConsoleContainer">
|
||||||
@@ -122,10 +118,6 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
<img src={infoBubbleIcon} alt="Info items" />
|
<img src={infoBubbleIcon} alt="Info items" />
|
||||||
<span className="numInfoItems">{numInfoItems}</span>
|
<span className="numInfoItems">{numInfoItems}</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="notificationConsoleHeaderIconWithData">
|
|
||||||
<img src={WarningIcon} alt="Warning items" />
|
|
||||||
<span className="numWarningItems">{numWarningItems}</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
{userContext.features.pr && <PrPreview pr={userContext.features.pr} />}
|
{userContext.features.pr && <PrPreview pr={userContext.features.pr} />}
|
||||||
<span className="consoleSplitter" />
|
<span className="consoleSplitter" />
|
||||||
@@ -206,7 +198,6 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
{item.type === ConsoleDataType.Info && <img className="infoIcon" src={InfoIcon} alt="info" />}
|
{item.type === ConsoleDataType.Info && <img className="infoIcon" src={InfoIcon} alt="info" />}
|
||||||
{item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
|
{item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
|
||||||
{item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
|
{item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
|
||||||
{item.type === ConsoleDataType.Warning && <img className="warningIcon" src={WarningIcon} alt="warning" />}
|
|
||||||
<span className="date">{item.date}</span>
|
<span className="date">{item.date}</span>
|
||||||
<span className="message" role="alert" aria-live="assertive">
|
<span className="message" role="alert" aria-live="assertive">
|
||||||
{item.message}
|
{item.message}
|
||||||
|
|||||||
@@ -59,19 +59,6 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
|||||||
0
|
0
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
className="notificationConsoleHeaderIconWithData"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="Warning items"
|
|
||||||
src={{}}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="numWarningItems"
|
|
||||||
>
|
|
||||||
0
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="consoleSplitter"
|
className="consoleSplitter"
|
||||||
@@ -242,19 +229,6 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
|||||||
1
|
1
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
className="notificationConsoleHeaderIconWithData"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="Warning items"
|
|
||||||
src={{}}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="numWarningItems"
|
|
||||||
>
|
|
||||||
0
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="consoleSplitter"
|
className="consoleSplitter"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Notebook container related stuff
|
* Notebook container related stuff
|
||||||
*/
|
*/
|
||||||
import { useDialog } from "Explorer/Controls/Dialog";
|
import { useDialog } from "Explorer/Controls/Dialog";
|
||||||
import promiseRetry, { AbortError, Options } from "p-retry";
|
import promiseRetry, { AbortError } from "p-retry";
|
||||||
import { PhoenixClient } from "Phoenix/PhoenixClient";
|
import { PhoenixClient } from "Phoenix/PhoenixClient";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { ConnectionStatusType, HttpHeaders, HttpStatusCodes, Notebook, PoolIdType } from "../../Common/Constants";
|
import { ConnectionStatusType, HttpHeaders, HttpStatusCodes, Notebook, PoolIdType } from "../../Common/Constants";
|
||||||
@@ -19,7 +19,7 @@ export class NotebookContainerClient {
|
|||||||
private clearReconnectionAttemptMessage? = () => {};
|
private clearReconnectionAttemptMessage? = () => {};
|
||||||
private isResettingWorkspace: boolean;
|
private isResettingWorkspace: boolean;
|
||||||
private phoenixClient: PhoenixClient;
|
private phoenixClient: PhoenixClient;
|
||||||
private retryOptions: Options;
|
private retryOptions: promiseRetry.Options;
|
||||||
private scheduleTimerId: NodeJS.Timeout;
|
private scheduleTimerId: NodeJS.Timeout;
|
||||||
|
|
||||||
constructor(private onConnectionLost: () => void) {
|
constructor(private onConnectionLost: () => void) {
|
||||||
|
|||||||
@@ -188,11 +188,6 @@ function openPane(action: ActionContracts.OpenPane, explorer: Explorer) {
|
|||||||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.AddCollection]
|
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.AddCollection]
|
||||||
) {
|
) {
|
||||||
explorer.onNewCollectionClicked();
|
explorer.onNewCollectionClicked();
|
||||||
} else if (
|
|
||||||
action.paneKind === ActionContracts.PaneKind.QuickStart ||
|
|
||||||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.QuickStart]
|
|
||||||
) {
|
|
||||||
explorer.onNewCollectionClicked({ isQuickstart: true });
|
|
||||||
} else if (
|
} else if (
|
||||||
action.paneKind === ActionContracts.PaneKind.CassandraAddCollection ||
|
action.paneKind === ActionContracts.PaneKind.CassandraAddCollection ||
|
||||||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.CassandraAddCollection]
|
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.CassandraAddCollection]
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { FullTextPoliciesComponent } from "Explorer/Controls/FullTextSeach/FullT
|
|||||||
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
||||||
import {
|
import {
|
||||||
AllPropertiesIndexed,
|
AllPropertiesIndexed,
|
||||||
AnalyticalStoreHeader,
|
AnalyticalStorageContent,
|
||||||
ContainerVectorPolicyTooltipContent,
|
ContainerVectorPolicyTooltipContent,
|
||||||
FullTextPolicyDefault,
|
FullTextPolicyDefault,
|
||||||
getPartitionKey,
|
getPartitionKey,
|
||||||
@@ -49,12 +49,16 @@ import { Action } from "Shared/Telemetry/TelemetryConstants";
|
|||||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { getCollectionName } from "Utils/APITypeUtils";
|
import { getCollectionName } from "Utils/APITypeUtils";
|
||||||
import { isCapabilityEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
import {
|
||||||
|
isCapabilityEnabled,
|
||||||
|
isFullTextSearchEnabled,
|
||||||
|
isServerlessAccount,
|
||||||
|
isVectorSearchEnabled,
|
||||||
|
} from "Utils/CapabilityUtils";
|
||||||
import { getUpsellMessage } from "Utils/PricingUtils";
|
import { getUpsellMessage } from "Utils/PricingUtils";
|
||||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
|
||||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
|
||||||
import { CollapsibleSectionComponent } from "../../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
import { CollapsibleSectionComponent } from "../../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
||||||
|
import "../../Controls/ThroughputInput/ThroughputInput.less";
|
||||||
import { ContainerSampleGenerator } from "../../DataSamples/ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "../../DataSamples/ContainerSampleGenerator";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useDatabases } from "../../useDatabases";
|
import { useDatabases } from "../../useDatabases";
|
||||||
@@ -106,7 +110,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
private collectionThroughput: number;
|
private collectionThroughput: number;
|
||||||
private isCollectionAutoscale: boolean;
|
private isCollectionAutoscale: boolean;
|
||||||
private isCostAcknowledged: boolean;
|
private isCostAcknowledged: boolean;
|
||||||
private showFullTextSearch: boolean;
|
|
||||||
|
|
||||||
constructor(props: AddCollectionPanelProps) {
|
constructor(props: AddCollectionPanelProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -123,7 +126,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
isSharded: userContext.apiType !== "Tables",
|
isSharded: userContext.apiType !== "Tables",
|
||||||
partitionKey: getPartitionKey(props.isQuickstart),
|
partitionKey: getPartitionKey(props.isQuickstart),
|
||||||
subPartitionKeys: [],
|
subPartitionKeys: [],
|
||||||
enableDedicatedThroughput: isFabricNative(), // Dedicated throughput is only enabled in Fabric Native by default
|
enableDedicatedThroughput: false,
|
||||||
createMongoWildCardIndex:
|
createMongoWildCardIndex:
|
||||||
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
||||||
useHashV1: false,
|
useHashV1: false,
|
||||||
@@ -141,8 +144,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
fullTextIndexes: [],
|
fullTextIndexes: [],
|
||||||
fullTextPolicyValidated: true,
|
fullTextPolicyValidated: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.showFullTextSearch = userContext.apiType === "SQL";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -265,7 +266,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
|
|
||||||
<div className="panelMainContent">
|
<div className="panelMainContent">
|
||||||
{!(isFabricNative() && this.props.databaseId !== undefined) && (
|
{!(isFabricNative() && this.props.databaseId !== undefined) && (
|
||||||
<Stack hidden={userContext.apiType === "Tables"} style={{ marginBottom: -2 }}>
|
<Stack hidden={userContext.apiType === "Tables"}>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
@@ -330,12 +331,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern={ValidCosmosDbIdInputPattern.source}
|
pattern="[^/?#\\]*[^/?# \\]"
|
||||||
title={ValidCosmosDbIdDescription}
|
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||||
placeholder="Type a new database id"
|
placeholder="Type a new database id"
|
||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
aria-label="New database id, Type a new database id"
|
aria-label="New database id, Type a new database id"
|
||||||
|
autoFocus
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
value={this.state.newDatabaseId}
|
value={this.state.newDatabaseId}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
@@ -406,12 +408,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
responsiveMode={999}
|
responsiveMode={999}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Separator className="panelSeparator" style={{ marginTop: -4, marginBottom: -4 }} />
|
<Separator className="panelSeparator" />
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal style={{ marginTop: -5, marginBottom: 1 }}>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
{`${getCollectionName()} id`}
|
{`${getCollectionName()} id`}
|
||||||
@@ -437,8 +439,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
aria-required
|
aria-required
|
||||||
required
|
required
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern={ValidCosmosDbIdInputPattern.source}
|
pattern="[^/?#\\]*[^/?# \\]"
|
||||||
title={ValidCosmosDbIdDescription}
|
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||||
placeholder={`e.g., ${getCollectionName()}1`}
|
placeholder={`e.g., ${getCollectionName()}1`}
|
||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
@@ -448,12 +450,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
this.setState({ collectionId: event.target.value })
|
this.setState({ collectionId: event.target.value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Separator className="panelSeparator" style={{ marginTop: -5, marginBottom: -5 }} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{this.shouldShowIndexingOptionsForFreeTierAccount() && (
|
{this.shouldShowIndexingOptionsForFreeTierAccount() && (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal style={{ marginTop: -4, marginBottom: -5 }}>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
Indexing
|
Indexing
|
||||||
@@ -499,7 +500,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
(!this.state.isSharedThroughputChecked ||
|
(!this.state.isSharedThroughputChecked ||
|
||||||
this.props.explorer.isFixedCollectionWithSharedThroughputSupported()) && (
|
this.props.explorer.isFixedCollectionWithSharedThroughputSupported()) && (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
Sharding
|
Sharding
|
||||||
@@ -555,7 +556,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
|
|
||||||
{this.state.isSharded && (
|
{this.state.isSharded && (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}>
|
<Stack horizontal>
|
||||||
<span className="mandatoryStar">* </span>
|
<span className="mandatoryStar">* </span>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
{getPartitionKeyName()}
|
{getPartitionKeyName()}
|
||||||
@@ -599,7 +600,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
{userContext.apiType === "SQL" &&
|
{userContext.apiType === "SQL" &&
|
||||||
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
|
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
|
||||||
return (
|
return (
|
||||||
<Stack style={{ marginBottom: 2, marginTop: -5 }} key={`uniqueKey${index}`} horizontal>
|
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${index}`} horizontal>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: "20px",
|
width: "20px",
|
||||||
@@ -645,7 +646,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{userContext.apiType === "SQL" && (
|
{!isFabricNative() && userContext.apiType === "SQL" && (
|
||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<DefaultButton
|
<DefaultButton
|
||||||
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
||||||
@@ -667,7 +668,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
<Separator className="panelSeparator" style={{ marginTop: 2, marginBottom: -4 }} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -709,7 +709,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.shouldShowCollectionThroughputInput() && !isFabricNative() && (
|
{this.shouldShowCollectionThroughputInput() && (
|
||||||
<ThroughputInput
|
<ThroughputInput
|
||||||
showFreeTierExceedThroughputTooltip={isFreeTierAccount() && !isFirstResourceCreated}
|
showFreeTierExceedThroughputTooltip={isFreeTierAccount() && !isFirstResourceCreated}
|
||||||
isDatabase={false}
|
isDatabase={false}
|
||||||
@@ -728,7 +728,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isFabricNative() && userContext.apiType === "SQL" && (
|
{!isFabricNative() && userContext.apiType === "SQL" && (
|
||||||
<Stack style={{ marginTop: -2, marginBottom: -4 }}>
|
<Stack>
|
||||||
{UniqueKeysHeader()}
|
{UniqueKeysHeader()}
|
||||||
{this.state.uniqueKeys.map((uniqueKey: string, i: number): JSX.Element => {
|
{this.state.uniqueKeys.map((uniqueKey: string, i: number): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
@@ -742,6 +742,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
: "Comma separated paths e.g. /firstName,/address/zipCode"
|
: "Comma separated paths e.g. /firstName,/address/zipCode"
|
||||||
}
|
}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
|
autoFocus
|
||||||
value={uniqueKey}
|
value={uniqueKey}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => {
|
const uniqueKeys = this.state.uniqueKeys.map((uniqueKey: string, j: number) => {
|
||||||
@@ -776,14 +777,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isFabricNative() && userContext.apiType === "SQL" && (
|
|
||||||
<Separator className="panelSeparator" style={{ marginTop: -15, marginBottom: -4 }} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{shouldShowAnalyticalStoreOptions() && (
|
{shouldShowAnalyticalStoreOptions() && (
|
||||||
<Stack className="panelGroupSpacing" style={{ marginTop: -4 }}>
|
<Stack className="panelGroupSpacing">
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
{AnalyticalStoreHeader()}
|
{AnalyticalStorageContent()}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Stack horizontal verticalAlign="center">
|
<Stack horizontal verticalAlign="center">
|
||||||
@@ -824,7 +821,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<Text variant="small">
|
<Text variant="small">
|
||||||
Azure Synapse Link is required for creating an analytical store{" "}
|
Azure Synapse Link is required for creating an analytical store{" "}
|
||||||
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account. <br />
|
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account.{" "}
|
||||||
<Link
|
<Link
|
||||||
href="https://aka.ms/cosmosdb-synapselink"
|
href="https://aka.ms/cosmosdb-synapselink"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -1134,7 +1131,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
private shouldShowCollectionThroughputInput(): boolean {
|
private shouldShowCollectionThroughputInput(): boolean {
|
||||||
if (isServerlessAccount()) {
|
if (isFabricNative() || isServerlessAccount()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1164,7 +1161,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowFullTextSearchParameters() {
|
private shouldShowFullTextSearchParameters() {
|
||||||
return !isFabricNative() && this.showFullTextSearch;
|
return isFullTextSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
|
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
|
||||||
@@ -1319,7 +1316,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.showFullTextSearch) {
|
if (this.shouldShowFullTextSearchParameters()) {
|
||||||
indexingPolicy.fullTextIndexes = this.state.fullTextIndexes;
|
indexingPolicy.fullTextIndexes = this.state.fullTextIndexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1353,12 +1350,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
let offerThroughput: number;
|
let offerThroughput: number;
|
||||||
let autoPilotMaxThroughput: number;
|
let autoPilotMaxThroughput: number;
|
||||||
|
|
||||||
// Throughput
|
if (databaseLevelThroughput) {
|
||||||
if (isFabricNative()) {
|
|
||||||
// Fabric Native accounts are always autoscale and have a fixed throughput of 5K
|
|
||||||
autoPilotMaxThroughput = AutoPilotUtils.autoPilotThroughput5K;
|
|
||||||
offerThroughput = undefined;
|
|
||||||
} else if (databaseLevelThroughput) {
|
|
||||||
if (this.state.createNewDatabase) {
|
if (this.state.createNewDatabase) {
|
||||||
if (this.isNewDatabaseAutoscale) {
|
if (this.isNewDatabaseAutoscale) {
|
||||||
autoPilotMaxThroughput = this.newDatabaseThroughput;
|
autoPilotMaxThroughput = this.newDatabaseThroughput;
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export function UniqueKeysHeader(): JSX.Element {
|
|||||||
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key.";
|
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key.";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack horizontal style={{ marginBottom: -2 }}>
|
<Stack horizontal>
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
Unique keys
|
Unique keys
|
||||||
</Text>
|
</Text>
|
||||||
@@ -98,21 +98,6 @@ export function shouldShowAnalyticalStoreOptions(): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AnalyticalStoreHeader(): JSX.Element {
|
|
||||||
const tooltipContent =
|
|
||||||
"Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads.";
|
|
||||||
return (
|
|
||||||
<Stack horizontal style={{ marginBottom: -2 }}>
|
|
||||||
<Text className="panelTextBold" variant="small">
|
|
||||||
Analytical Store
|
|
||||||
</Text>
|
|
||||||
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={tooltipContent}>
|
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} ariaLabel={tooltipContent} />
|
|
||||||
</TooltipHost>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AnalyticalStorageContent(): JSX.Element {
|
export function AnalyticalStorageContent(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Text variant="small">
|
<Text variant="small">
|
||||||
|
|||||||
@@ -11,11 +11,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
<Stack
|
<Stack
|
||||||
hidden={false}
|
hidden={false}
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -93,11 +88,12 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
aria-label="New database id, Type a new database id"
|
aria-label="New database id, Type a new database id"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
autoFocus={true}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
id="newDatabaseId"
|
id="newDatabaseId"
|
||||||
name="newDatabaseId"
|
name="newDatabaseId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern="[^\\/?#\\\\]*[^\\/?# \\\\]"
|
pattern="[^/?#\\\\]*[^/?# \\\\]"
|
||||||
placeholder="Type a new database id"
|
placeholder="Type a new database id"
|
||||||
required={true}
|
required={true}
|
||||||
size={40}
|
size={40}
|
||||||
@@ -144,23 +140,11 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Separator
|
<Separator
|
||||||
className="panelSeparator"
|
className="panelSeparator"
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -4,
|
|
||||||
"marginTop": -4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": 1,
|
|
||||||
"marginTop": -5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="mandatoryStar"
|
className="mandatoryStar"
|
||||||
@@ -194,7 +178,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
id="collectionId"
|
id="collectionId"
|
||||||
name="collectionId"
|
name="collectionId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern="[^\\/?#\\\\]*[^\\/?# \\\\]"
|
pattern="[^/?#\\\\]*[^/?# \\\\]"
|
||||||
placeholder="e.g., Container1"
|
placeholder="e.g., Container1"
|
||||||
required={true}
|
required={true}
|
||||||
size={40}
|
size={40}
|
||||||
@@ -202,25 +186,10 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
<Separator
|
|
||||||
className="panelSeparator"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -5,
|
|
||||||
"marginTop": -5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -4,
|
|
||||||
"marginTop": -5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="mandatoryStar"
|
className="mandatoryStar"
|
||||||
@@ -285,15 +254,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
Add hierarchical partition key
|
Add hierarchical partition key
|
||||||
</CustomizedDefaultButton>
|
</CustomizedDefaultButton>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Separator
|
|
||||||
className="panelSeparator"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -4,
|
|
||||||
"marginTop": 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
<ThroughputInput
|
<ThroughputInput
|
||||||
isDatabase={false}
|
isDatabase={false}
|
||||||
@@ -303,21 +263,9 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
setIsThroughputCapExceeded={[Function]}
|
setIsThroughputCapExceeded={[Function]}
|
||||||
setThroughputValue={[Function]}
|
setThroughputValue={[Function]}
|
||||||
/>
|
/>
|
||||||
<Stack
|
<Stack>
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -4,
|
|
||||||
"marginTop": -2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
className="panelTextBold"
|
className="panelTextBold"
|
||||||
@@ -358,53 +306,26 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
Add unique key
|
Add unique key
|
||||||
</CustomizedActionButton>
|
</CustomizedActionButton>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Separator
|
|
||||||
className="panelSeparator"
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -4,
|
|
||||||
"marginTop": -15,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Stack
|
<Stack
|
||||||
className="panelGroupSpacing"
|
className="panelGroupSpacing"
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginTop": -4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
className="panelTextBold"
|
className="panelTextBold"
|
||||||
variant="small"
|
variant="small"
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"marginBottom": -2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
className="panelTextBold"
|
|
||||||
variant="small"
|
variant="small"
|
||||||
>
|
>
|
||||||
Analytical Store
|
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"
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</StyledLinkBase>
|
||||||
</Text>
|
</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>
|
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -461,7 +382,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
|
|
||||||
container
|
container
|
||||||
. Enable Synapse Link for this Cosmos DB account.
|
. Enable Synapse Link for this Cosmos DB account.
|
||||||
<br />
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
aria-label="Learn more about Azure Synapse Link."
|
aria-label="Learn more about Azure Synapse Link."
|
||||||
className="capacitycalculator-link"
|
className="capacitycalculator-link"
|
||||||
@@ -490,44 +411,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
|
||||||
<CollapsibleSectionComponent
|
|
||||||
isExpandedByDefault={false}
|
|
||||||
onExpand={[Function]}
|
|
||||||
title="Container Full Text Search Policy"
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
id="collapsibleFullTextPolicySectionContent"
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"position": "relative",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
styles={
|
|
||||||
{
|
|
||||||
"root": {
|
|
||||||
"paddingLeft": 40,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FullTextPoliciesComponent
|
|
||||||
fullTextPolicy={
|
|
||||||
{
|
|
||||||
"defaultLanguage": "en-US",
|
|
||||||
"fullTextPaths": [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onFullTextPathChange={[Function]}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</CollapsibleSectionComponent>
|
|
||||||
</Stack>
|
|
||||||
<CollapsibleSectionComponent
|
<CollapsibleSectionComponent
|
||||||
isExpandedByDefault={false}
|
isExpandedByDefault={false}
|
||||||
onExpand={[Function]}
|
onExpand={[Function]}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
|
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
|
||||||
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
|
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
|
||||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
@@ -205,8 +204,8 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
aria-required="true"
|
aria-required="true"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern={ValidCosmosDbIdInputPattern.source}
|
pattern="[^/?#\\]*[^/?# \\]"
|
||||||
title={ValidCosmosDbIdDescription}
|
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||||
size={40}
|
size={40}
|
||||||
aria-label={databaseIdLabel}
|
aria-label={databaseIdLabel}
|
||||||
placeholder={databaseIdPlaceHolder}
|
placeholder={databaseIdPlaceHolder}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ exports[`AddDatabasePane Pane should render Default properly 1`] = `
|
|||||||
data-lpignore={true}
|
data-lpignore={true}
|
||||||
id="database-id"
|
id="database-id"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern="[^\\/?#\\\\]*[^\\/?# \\\\]"
|
pattern="[^/?#\\\\]*[^/?# \\\\]"
|
||||||
placeholder="Type a new database id"
|
placeholder="Type a new database id"
|
||||||
size={40}
|
size={40}
|
||||||
styles={
|
styles={
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { shallow, ShallowWrapper } from "enzyme";
|
|
||||||
import Explorer from "Explorer/Explorer";
|
|
||||||
import {
|
|
||||||
AddGlobalSecondaryIndexPanel,
|
|
||||||
AddGlobalSecondaryIndexPanelProps,
|
|
||||||
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
|
||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
const props: AddGlobalSecondaryIndexPanelProps = {
|
|
||||||
explorer: new Explorer(),
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("AddGlobalSecondaryIndexPanel", () => {
|
|
||||||
it("render default panel", () => {
|
|
||||||
const wrapper: ShallowWrapper<AddGlobalSecondaryIndexPanelProps, object, Component> = shallow(
|
|
||||||
<AddGlobalSecondaryIndexPanel {...props} />,
|
|
||||||
);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render form", () => {
|
|
||||||
const wrapper: ShallowWrapper<AddGlobalSecondaryIndexPanelProps, object, Component> = shallow(
|
|
||||||
<AddGlobalSecondaryIndexPanel {...props} />,
|
|
||||||
);
|
|
||||||
const form = wrapper.find("form").first();
|
|
||||||
expect(form).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user