mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-07 19:46:53 +00:00
Compare commits
2 Commits
platform-f
...
index-arch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a96d679087 | ||
|
|
dc8bb69359 |
@@ -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
|
||||||
@@ -144,5 +146,3 @@ __mocks__/monaco-editor.ts
|
|||||||
src/Explorer/Tree/ResourceTree.tsx
|
src/Explorer/Tree/ResourceTree.tsx
|
||||||
src/Utils/EndpointUtils.ts
|
src/Utils/EndpointUtils.ts
|
||||||
src/Utils/PriorityBasedExecutionUtils.ts
|
src/Utils/PriorityBasedExecutionUtils.ts
|
||||||
|
|
||||||
utils/local-proxy/**
|
|
||||||
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@@ -177,27 +177,9 @@ jobs:
|
|||||||
- name: "Az CLI login"
|
- name: "Az CLI login"
|
||||||
uses: Azure/login@v2
|
uses: Azure/login@v2
|
||||||
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 }}
|
||||||
# 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 }} --workers=3
|
||||||
- name: Upload blob report to GitHub Actions Artifacts
|
- name: Upload blob report to GitHub Actions Artifacts
|
||||||
|
|||||||
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 }}
|
||||||
|
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -24,5 +24,8 @@
|
|||||||
"source.organizeImports": "explicit"
|
"source.organizeImports": "explicit"
|
||||||
},
|
},
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"[typescriptreact]": {
|
||||||
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"EMULATOR_ENDPOINT": "http://localhost:8081"
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
126
package-lock.json
generated
126
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.3.0",
|
||||||
"@azure/cosmos-language-service": "0.0.5",
|
"@azure/cosmos-language-service": "0.0.5",
|
||||||
"@azure/identity": "4.5.0",
|
"@azure/identity": "4.5.0",
|
||||||
"@azure/msal-browser": "2.14.2",
|
"@azure/msal-browser": "2.14.2",
|
||||||
@@ -205,58 +205,6 @@
|
|||||||
"webpack-dev-server": "4.15.2"
|
"webpack-dev-server": "4.15.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"canvas": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"extraneous": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"local_dependencies/@azure/cosmos": {
|
|
||||||
"version": "4.0.1-beta.3",
|
|
||||||
"extraneous": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/abort-controller": "^1.0.0",
|
|
||||||
"@azure/core-auth": "^1.3.0",
|
|
||||||
"@azure/core-rest-pipeline": "^1.2.0",
|
|
||||||
"@azure/core-tracing": "^1.0.0",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"fast-json-stable-stringify": "^2.1.0",
|
|
||||||
"jsbi": "^3.1.3",
|
|
||||||
"node-abort-controller": "^3.0.0",
|
|
||||||
"priorityqueuejs": "^2.0.0",
|
|
||||||
"semaphore": "^1.0.5",
|
|
||||||
"tslib": "^2.2.0",
|
|
||||||
"universal-user-agent": "^6.0.0",
|
|
||||||
"uuid": "^8.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"local_dependencies/cosmos": {
|
|
||||||
"name": "@azure/cosmos",
|
|
||||||
"version": "4.0.1-beta.3",
|
|
||||||
"extraneous": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/abort-controller": "^1.0.0",
|
|
||||||
"@azure/core-auth": "^1.3.0",
|
|
||||||
"@azure/core-rest-pipeline": "^1.2.0",
|
|
||||||
"@azure/core-tracing": "^1.0.0",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"fast-json-stable-stringify": "^2.1.0",
|
|
||||||
"jsbi": "^3.1.3",
|
|
||||||
"node-abort-controller": "^3.0.0",
|
|
||||||
"priorityqueuejs": "^2.0.0",
|
|
||||||
"semaphore": "^1.0.5",
|
|
||||||
"tslib": "^2.2.0",
|
|
||||||
"universal-user-agent": "^6.0.0",
|
|
||||||
"uuid": "^8.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@aashutoshrathi/word-wrap": {
|
"node_modules/@aashutoshrathi/word-wrap": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -443,31 +391,24 @@
|
|||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/@azure/cosmos": {
|
"node_modules/@azure/cosmos": {
|
||||||
<<<<<<< HEAD
|
"version": "4.3.0",
|
||||||
"version": "4.2.0-beta.1",
|
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.3.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz",
|
"integrity": "sha512-0Ls3l1uWBBSphx6YRhnM+w7rSvq8qVugBCdO6kSiNuRYXEf6+YWLjbzz4e7L2kkz/6ScFdZIOJYP+XtkiRYOhA==",
|
||||||
"integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==",
|
|
||||||
=======
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-JsTh4twb6FcwP7rJwxQiNZQ/LGtuF6gmciaxY9Rnp6/A325Lhsw/SH4R2ArpT0yCvozbZpweIwdPfUkXVBtp5w==",
|
|
||||||
>>>>>>> master
|
|
||||||
"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/keyvault-keys": "^4.8.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": {
|
||||||
@@ -497,14 +438,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",
|
|
||||||
<<<<<<< HEAD
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
=======
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
>>>>>>> master
|
|
||||||
},
|
},
|
||||||
"node_modules/@azure/identity": {
|
"node_modules/@azure/identity": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
@@ -7956,7 +7891,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nteract/data-explorer/node_modules/cross-spawn": {
|
"node_modules/@nteract/data-explorer/node_modules/cross-spawn": {
|
||||||
"version": "7.0.5",
|
"version": "6.0.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nice-try": "^1.0.4",
|
"nice-try": "^1.0.4",
|
||||||
@@ -8080,7 +8015,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^6.0.0",
|
||||||
"get-stream": "^4.0.0",
|
"get-stream": "^4.0.0",
|
||||||
"is-stream": "^1.1.0",
|
"is-stream": "^1.1.0",
|
||||||
"npm-run-path": "^2.0.0",
|
"npm-run-path": "^2.0.0",
|
||||||
@@ -16508,7 +16443,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.5",
|
"version": "7.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
@@ -18422,7 +18357,7 @@
|
|||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^7.0.2",
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
"doctrine": "^3.0.0",
|
"doctrine": "^3.0.0",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
@@ -19009,7 +18944,7 @@
|
|||||||
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^7.0.3",
|
||||||
"get-stream": "^6.0.0",
|
"get-stream": "^6.0.0",
|
||||||
"human-signals": "^2.1.0",
|
"human-signals": "^2.1.0",
|
||||||
"is-stream": "^2.0.0",
|
"is-stream": "^2.0.0",
|
||||||
@@ -19274,7 +19209,7 @@
|
|||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-to-regexp": "0.1.12",
|
"path-to-regexp": "0.1.7",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.11.0",
|
"qs": "6.11.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
@@ -19310,7 +19245,7 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/express/node_modules/path-to-regexp": {
|
"node_modules/express/node_modules/path-to-regexp": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.7",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -27243,6 +27178,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"
|
||||||
@@ -30598,7 +30538,7 @@
|
|||||||
"@yarnpkg/lockfile": "^1.1.0",
|
"@yarnpkg/lockfile": "^1.1.0",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"ci-info": "^3.7.0",
|
"ci-info": "^3.7.0",
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^7.0.3",
|
||||||
"find-yarn-workspace-root": "^2.0.0",
|
"find-yarn-workspace-root": "^2.0.0",
|
||||||
"fs-extra": "^9.0.0",
|
"fs-extra": "^9.0.0",
|
||||||
"json-stable-stringify": "^1.0.2",
|
"json-stable-stringify": "^1.0.2",
|
||||||
@@ -31576,7 +31516,7 @@
|
|||||||
"address": "^1.1.2",
|
"address": "^1.1.2",
|
||||||
"browserslist": "^4.18.1",
|
"browserslist": "^4.18.1",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^7.0.3",
|
||||||
"detect-port-alt": "^1.1.6",
|
"detect-port-alt": "^1.1.6",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"filesize": "^8.0.6",
|
"filesize": "^8.0.6",
|
||||||
@@ -33032,7 +32972,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sane/node_modules/cross-spawn": {
|
"node_modules/sane/node_modules/cross-spawn": {
|
||||||
"version": "7.0.5",
|
"version": "6.0.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nice-try": "^1.0.4",
|
"nice-try": "^1.0.4",
|
||||||
@@ -33049,7 +32989,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^6.0.0",
|
||||||
"get-stream": "^4.0.0",
|
"get-stream": "^4.0.0",
|
||||||
"is-stream": "^1.1.0",
|
"is-stream": "^1.1.0",
|
||||||
"npm-run-path": "^2.0.0",
|
"npm-run-path": "^2.0.0",
|
||||||
@@ -36018,7 +35958,7 @@
|
|||||||
"@webpack-cli/serve": "^2.0.5",
|
"@webpack-cli/serve": "^2.0.5",
|
||||||
"colorette": "^2.0.14",
|
"colorette": "^2.0.14",
|
||||||
"commander": "^10.0.1",
|
"commander": "^10.0.1",
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^7.0.3",
|
||||||
"envinfo": "^7.7.3",
|
"envinfo": "^7.7.3",
|
||||||
"fastest-levenshtein": "^1.0.12",
|
"fastest-levenshtein": "^1.0.12",
|
||||||
"import-local": "^3.0.2",
|
"import-local": "^3.0.2",
|
||||||
@@ -36543,7 +36483,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/windows-release/node_modules/cross-spawn": {
|
"node_modules/windows-release/node_modules/cross-spawn": {
|
||||||
"version": "7.0.5",
|
"version": "6.0.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nice-try": "^1.0.4",
|
"nice-try": "^1.0.4",
|
||||||
@@ -36560,7 +36500,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.5",
|
"cross-spawn": "^6.0.0",
|
||||||
"get-stream": "^4.0.0",
|
"get-stream": "^4.0.0",
|
||||||
"is-stream": "^1.1.0",
|
"is-stream": "^1.1.0",
|
||||||
"npm-run-path": "^2.0.0",
|
"npm-run-path": "^2.0.0",
|
||||||
|
|||||||
@@ -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.3.0",
|
||||||
"@azure/cosmos-language-service": "0.0.5",
|
"@azure/cosmos-language-service": "0.0.5",
|
||||||
"@azure/identity": "4.5.0",
|
"@azure/identity": "4.5.0",
|
||||||
"@azure/msal-browser": "2.14.2",
|
"@azure/msal-browser": "2.14.2",
|
||||||
@@ -206,11 +206,9 @@
|
|||||||
"build:dataExplorer:ci": "npm run build:ci",
|
"build:dataExplorer:ci": "npm run build:ci",
|
||||||
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
|
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
|
||||||
"build:ci": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
|
"build:ci": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
|
||||||
"build:proxy": "npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToProxy",
|
|
||||||
"pack:prod": "webpack --mode production",
|
"pack:prod": "webpack --mode production",
|
||||||
"pack:fast": "webpack --mode development --progress",
|
"pack:fast": "webpack --mode development --progress",
|
||||||
"copyToConsumers": "node copyToConsumers",
|
"copyToConsumers": "node copyToConsumers",
|
||||||
"copyToProxy": "rm -rf ./utils/local-proxy/dist && cp -r ./dist ./utils/local-proxy",
|
|
||||||
"test": "rimraf coverage && jest",
|
"test": "rimraf coverage && jest",
|
||||||
"test:debug": "jest --runInBand",
|
"test:debug": "jest --runInBand",
|
||||||
"test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",
|
"test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",
|
||||||
|
|||||||
426
preview/package-lock.json
generated
426
preview/package-lock.json
generated
@@ -19077,9 +19077,9 @@
|
|||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-to-regexp": "0.1.12",
|
"path-to-regexp": "0.1.7",
|
||||||
"proxy-addr": "~2.0.5",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.7.0",
|
"qs": "6.11.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"send": "0.18.0",
|
"send": "0.18.0",
|
||||||
@@ -35537,420 +35537,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"node_modules/verror": {
|
||||||
"version": "14.14.37",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz",
|
"engines": [
|
||||||
"integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw=="
|
"node >=0.6.0"
|
||||||
},
|
],
|
||||||
"accepts": {
|
"license": "MIT",
|
||||||
"version": "1.3.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
|
||||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
|
||||||
"requires": {
|
|
||||||
"mime-types": "~2.1.24",
|
|
||||||
"negotiator": "0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"array-flatten": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
|
||||||
},
|
|
||||||
"body-parser": {
|
|
||||||
"version": "1.19.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
|
||||||
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
|
||||||
"requires": {
|
|
||||||
"bytes": "3.1.0",
|
|
||||||
"content-type": "~1.0.4",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"http-errors": "1.7.2",
|
|
||||||
"iconv-lite": "0.4.24",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"qs": "6.7.0",
|
|
||||||
"raw-body": "2.4.0",
|
|
||||||
"type-is": "~1.6.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"braces": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
|
||||||
"requires": {
|
|
||||||
"fill-range": "^7.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bytes": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
|
||||||
},
|
|
||||||
"camelcase": {
|
|
||||||
"version": "6.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
|
|
||||||
"integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg=="
|
|
||||||
},
|
|
||||||
"content-disposition": {
|
|
||||||
"version": "0.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
|
||||||
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "5.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"content-type": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
|
||||||
},
|
|
||||||
"cookie": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
|
||||||
},
|
|
||||||
"cookie-signature": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
|
||||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"depd": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
|
||||||
},
|
|
||||||
"destroy": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
|
||||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
|
||||||
},
|
|
||||||
"ee-first": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
|
||||||
},
|
|
||||||
"encodeurl": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
|
||||||
},
|
|
||||||
"escape-html": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
|
||||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
|
||||||
},
|
|
||||||
"etag": {
|
|
||||||
"version": "1.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
|
||||||
},
|
|
||||||
"eventemitter3": {
|
|
||||||
"version": "4.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
|
||||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
|
||||||
},
|
|
||||||
"express": {
|
|
||||||
"version": "4.17.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
|
||||||
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
|
||||||
"requires": {
|
|
||||||
"accepts": "~1.3.7",
|
|
||||||
"array-flatten": "1.1.1",
|
|
||||||
"body-parser": "1.19.0",
|
|
||||||
"content-disposition": "0.5.3",
|
|
||||||
"content-type": "~1.0.4",
|
|
||||||
"cookie": "0.4.0",
|
|
||||||
"cookie-signature": "1.0.6",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"finalhandler": "~1.1.2",
|
|
||||||
"fresh": "0.5.2",
|
|
||||||
"merge-descriptors": "1.0.1",
|
|
||||||
"methods": "~1.1.2",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"path-to-regexp": "0.1.12",
|
|
||||||
"proxy-addr": "~2.0.5",
|
|
||||||
"qs": "6.7.0",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"safe-buffer": "5.1.2",
|
|
||||||
"send": "0.17.1",
|
|
||||||
"serve-static": "1.14.1",
|
|
||||||
"setprototypeof": "1.1.1",
|
|
||||||
"statuses": "~1.5.0",
|
|
||||||
"type-is": "~1.6.18",
|
|
||||||
"utils-merge": "1.0.1",
|
|
||||||
"vary": "~1.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fill-range": {
|
|
||||||
"version": "7.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
|
||||||
"requires": {
|
|
||||||
"to-regex-range": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"finalhandler": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"statuses": "~1.5.0",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"follow-redirects": {
|
|
||||||
"version": "1.13.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz",
|
|
||||||
"integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA=="
|
|
||||||
},
|
|
||||||
"forwarded": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
|
||||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
|
||||||
},
|
|
||||||
"fresh": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
|
||||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
|
||||||
},
|
|
||||||
"http-errors": {
|
|
||||||
"version": "1.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
|
||||||
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
|
||||||
"requires": {
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"inherits": "2.0.3",
|
|
||||||
"setprototypeof": "1.1.1",
|
|
||||||
"statuses": ">= 1.5.0 < 2",
|
|
||||||
"toidentifier": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-proxy": {
|
|
||||||
"version": "1.18.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
|
|
||||||
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
|
|
||||||
"requires": {
|
|
||||||
"eventemitter3": "^4.0.0",
|
|
||||||
"follow-redirects": "^1.0.0",
|
|
||||||
"requires-port": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-proxy-middleware": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-OnjU5vyVgcZVe2AjLJyMrk8YLNOC2lspCHirB5ldM+B/dwEfZ5bgVTrFyzE9R7xRWAP/i/FXtvIqKjTNEZBhBg==",
|
|
||||||
"requires": {
|
|
||||||
"@types/http-proxy": "^1.17.5",
|
|
||||||
"camelcase": "^6.2.0",
|
|
||||||
"http-proxy": "^1.18.1",
|
|
||||||
"is-glob": "^4.0.1",
|
|
||||||
"is-plain-obj": "^3.0.0",
|
|
||||||
"micromatch": "^4.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"iconv-lite": {
|
|
||||||
"version": "0.4.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
|
||||||
"requires": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inherits": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
|
||||||
},
|
|
||||||
"ipaddr.js": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
|
||||||
},
|
|
||||||
"is-extglob": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
|
||||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
|
||||||
},
|
|
||||||
"is-glob": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
|
||||||
"requires": {
|
|
||||||
"is-extglob": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is-number": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
|
||||||
},
|
|
||||||
"is-plain-obj": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA=="
|
|
||||||
},
|
|
||||||
"media-typer": {
|
|
||||||
"version": "0.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
|
||||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
|
||||||
},
|
|
||||||
"merge-descriptors": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
|
||||||
},
|
|
||||||
"methods": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
|
||||||
},
|
|
||||||
"micromatch": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
|
|
||||||
"requires": {
|
|
||||||
"braces": "^3.0.1",
|
|
||||||
"picomatch": "^2.0.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mime": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
|
||||||
},
|
|
||||||
"mime-db": {
|
|
||||||
"version": "1.46.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
|
|
||||||
"integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ=="
|
|
||||||
},
|
|
||||||
"mime-types": {
|
|
||||||
"version": "2.1.29",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
|
|
||||||
"integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
|
|
||||||
"requires": {
|
|
||||||
"mime-db": "1.46.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
|
||||||
},
|
|
||||||
"negotiator": {
|
|
||||||
"version": "0.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
|
||||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
|
||||||
},
|
|
||||||
"node-fetch": {
|
|
||||||
"version": "2.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
|
||||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
|
||||||
},
|
|
||||||
"on-finished": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
|
||||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
|
||||||
"requires": {
|
|
||||||
"ee-first": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parseurl": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
|
||||||
},
|
|
||||||
"path-to-regexp": {
|
|
||||||
"version": "0.1.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
|
||||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
|
|
||||||
},
|
|
||||||
"picomatch": {
|
|
||||||
"version": "2.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
|
||||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
|
|
||||||
},
|
|
||||||
"proxy-addr": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
|
||||||
"requires": {
|
|
||||||
"forwarded": "~0.1.2",
|
|
||||||
"ipaddr.js": "1.9.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"qs": {
|
|
||||||
"version": "6.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
|
||||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
|
||||||
},
|
|
||||||
"range-parser": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
|
||||||
},
|
|
||||||
"raw-body": {
|
|
||||||
"version": "2.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
|
||||||
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
|
||||||
"requires": {
|
|
||||||
"bytes": "3.1.0",
|
|
||||||
"http-errors": "1.7.2",
|
|
||||||
"iconv-lite": "0.4.24",
|
|
||||||
"unpipe": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"requires-port": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
|
||||||
},
|
|
||||||
"safe-buffer": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
|
||||||
},
|
|
||||||
"safer-buffer": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"version": "0.17.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
|
||||||
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"destroy": "~1.0.4",
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"fresh": "0.5.2",
|
|
||||||
"http-errors": "~1.7.2",
|
|
||||||
"mime": "1.6.0",
|
|
||||||
"ms": "2.1.1",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"statuses": "~1.5.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert-plus": "^1.0.0",
|
"assert-plus": "^1.0.0",
|
||||||
"core-util-is": "1.0.2",
|
"core-util-is": "1.0.2",
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
|
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
|
||||||
{
|
{
|
||||||
|
"disableNonStreamingOrderByQuery": true,
|
||||||
"enableQueryControl": false,
|
"enableQueryControl": false,
|
||||||
"enableScanInQuery": true,
|
"enableScanInQuery": true,
|
||||||
"forceQueryPlan": true,
|
"forceQueryPlan": true,
|
||||||
@@ -13,6 +14,7 @@ exports[`getCommonQueryOptions builds the correct default options objects 1`] =
|
|||||||
|
|
||||||
exports[`getCommonQueryOptions reads from localStorage 1`] = `
|
exports[`getCommonQueryOptions reads from localStorage 1`] = `
|
||||||
{
|
{
|
||||||
|
"disableNonStreamingOrderByQuery": true,
|
||||||
"enableQueryControl": false,
|
"enableQueryControl": false,
|
||||||
"enableScanInQuery": true,
|
"enableScanInQuery": true,
|
||||||
"forceQueryPlan": true,
|
"forceQueryPlan": true,
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -27,5 +28,6 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
|
|||||||
Queries.itemsPerPage;
|
Queries.itemsPerPage;
|
||||||
options.enableQueryControl = LocalStorageUtility.getEntryBoolean(StorageKey.QueryControlEnabled);
|
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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -19,13 +25,10 @@ export enum Platform {
|
|||||||
Hosted = "Hosted",
|
Hosted = "Hosted",
|
||||||
Emulator = "Emulator",
|
Emulator = "Emulator",
|
||||||
Fabric = "Fabric",
|
Fabric = "Fabric",
|
||||||
VNextEmulator = "VNextEmulator",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>;
|
||||||
@@ -34,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
|
||||||
@@ -45,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,
|
||||||
@@ -77,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/",
|
||||||
@@ -96,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 {
|
||||||
@@ -111,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,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,
|
||||||
@@ -226,7 +232,6 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
|||||||
case Platform.Fabric:
|
case Platform.Fabric:
|
||||||
case Platform.Hosted:
|
case Platform.Hosted:
|
||||||
case Platform.Emulator:
|
case Platform.Emulator:
|
||||||
case Platform.VNextEmulator:
|
|
||||||
updateConfigContext({ platform });
|
updateConfigContext({ platform });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export enum PaneKind {
|
|||||||
GlobalSettings,
|
GlobalSettings,
|
||||||
AdHocAccess,
|
AdHocAccess,
|
||||||
SwitchDirectory,
|
SwitchDirectory,
|
||||||
QuickStart,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export interface ArmEntity {
|
|||||||
type: string;
|
type: string;
|
||||||
kind: string;
|
kind: string;
|
||||||
tags?: Tags;
|
tags?: Tags;
|
||||||
resourceGroup?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseAccount extends ArmEntity {
|
export interface DatabaseAccount extends ArmEntity {
|
||||||
@@ -389,7 +388,7 @@ export interface VectorEmbeddingPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface VectorEmbedding {
|
export interface VectorEmbedding {
|
||||||
dataType: "float32" | "uint8" | "int8";
|
dataType: "float16" | "float32" | "uint8" | "int8";
|
||||||
dimensions: number;
|
dimensions: number;
|
||||||
distanceFunction: "euclidean" | "cosine" | "dotproduct";
|
distanceFunction: "euclidean" | "cosine" | "dotproduct";
|
||||||
path: string;
|
path: string;
|
||||||
|
|||||||
@@ -443,7 +443,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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@ import { useDatabases } from "Explorer/useDatabases";
|
|||||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||||
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
||||||
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import DiscardIcon from "../../../../images/discard.svg";
|
import DiscardIcon from "../../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||||
@@ -61,15 +60,15 @@ import {
|
|||||||
AddMongoIndexProps,
|
AddMongoIndexProps,
|
||||||
ChangeFeedPolicyState,
|
ChangeFeedPolicyState,
|
||||||
GeospatialConfigType,
|
GeospatialConfigType,
|
||||||
|
MongoIndexTypes,
|
||||||
|
SettingsV2TabTypes,
|
||||||
|
TtlType,
|
||||||
getMongoNotification,
|
getMongoNotification,
|
||||||
getTabTitle,
|
getTabTitle,
|
||||||
hasDatabaseSharedThroughput,
|
hasDatabaseSharedThroughput,
|
||||||
isDirty,
|
isDirty,
|
||||||
MongoIndexTypes,
|
|
||||||
parseConflictResolutionMode,
|
parseConflictResolutionMode,
|
||||||
parseConflictResolutionProcedure,
|
parseConflictResolutionProcedure,
|
||||||
SettingsV2TabTypes,
|
|
||||||
TtlType,
|
|
||||||
} from "./SettingsUtils";
|
} from "./SettingsUtils";
|
||||||
|
|
||||||
interface SettingsV2TabInfo {
|
interface SettingsV2TabInfo {
|
||||||
@@ -277,14 +276,14 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
this.saveSettingsButton = {
|
this.saveSettingsButton = {
|
||||||
isEnabled: this.isSaveSettingsButtonEnabled,
|
isEnabled: this.isSaveSettingsButtonEnabled,
|
||||||
isVisible: () => {
|
isVisible: () => {
|
||||||
return isFeatureSupported(PlatformFeature.UpdateCollection);
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.discardSettingsChangesButton = {
|
this.discardSettingsChangesButton = {
|
||||||
isEnabled: this.isDiscardSettingsButtonEnabled,
|
isEnabled: this.isDiscardSettingsButtonEnabled,
|
||||||
isVisible: () => {
|
isVisible: () => {
|
||||||
return isFeatureSupported(PlatformFeature.UpdateCollection);
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -559,69 +559,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 +579,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
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ 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 "./ThroughputInput.less";
|
import "./ThroughputInput.less";
|
||||||
|
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
|
||||||
|
|
||||||
export interface ThroughputInputProps {
|
export interface ThroughputInputProps {
|
||||||
isDatabase: boolean;
|
isDatabase: boolean;
|
||||||
@@ -41,12 +41,11 @@ 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) ||
|
||||||
|
isFabricNative()
|
||||||
) {
|
) {
|
||||||
defaultThroughput = AutoPilotUtils.autoPilotThroughput1K;
|
defaultThroughput = AutoPilotUtils.autoPilotThroughput1K;
|
||||||
} else if (workloadType === Constants.WorkloadType.Production) {
|
} else if (workloadType === Constants.WorkloadType.Production) {
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,22 +3,16 @@ import { Link } from "@fluentui/react/lib/Link";
|
|||||||
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
||||||
import { Environment, getEnvironment } from "Common/EnvironmentUtility";
|
import { Environment, getEnvironment } from "Common/EnvironmentUtility";
|
||||||
import { sendMessage } from "Common/MessageHandler";
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
import { configContext, Platform } from "ConfigContext";
|
import { Platform, configContext } from "ConfigContext";
|
||||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
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 { featureRegistered } from "Utils/FeatureRegistrationUtils";
|
||||||
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
|
|
||||||
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";
|
||||||
@@ -290,40 +284,14 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public openInVsCode(): void {
|
||||||
* 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 activeTab = useTabs.getState().activeTab;
|
||||||
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
|
const resourceId = encodeURIComponent(userContext.databaseAccount.id);
|
||||||
const database = encodeURIComponent(activeTab?.collection?.databaseId);
|
const database = encodeURIComponent(activeTab?.collection?.databaseId);
|
||||||
const container = encodeURIComponent(activeTab?.collection?.id());
|
const container = encodeURIComponent(activeTab?.collection?.id());
|
||||||
const baseUrl = `vscode://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
|
const baseUrl = `vscode://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`;
|
||||||
const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl;
|
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 = {
|
const openVSCodeDialogProps: DialogProps = {
|
||||||
linkProps: {
|
linkProps: {
|
||||||
linkText: "Download Visual Studio Code",
|
linkText: "Download Visual Studio Code",
|
||||||
@@ -1181,14 +1149,10 @@ 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 =
|
||||||
isFeatureSupported(PlatformFeature.Notebooks) &&
|
|
||||||
configContext.platform !== Platform.Fabric &&
|
configContext.platform !== Platform.Fabric &&
|
||||||
(userContext.features.notebooksDownBanner ||
|
(userContext.features.notebooksDownBanner ||
|
||||||
useNotebook.getState().isPhoenixNotebooks ||
|
useNotebook.getState().isPhoenixNotebooks ||
|
||||||
@@ -1196,11 +1160,7 @@ export default class Explorer {
|
|||||||
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
|
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
|
||||||
useNotebook
|
useNotebook
|
||||||
.getState()
|
.getState()
|
||||||
.setIsShellEnabled(
|
.setIsShellEnabled(useNotebook.getState().isPhoenixFeatures && isPublicInternetAccessAllowed());
|
||||||
isFeatureSupported(PlatformFeature.CloudShell) &&
|
|
||||||
useNotebook.getState().isPhoenixFeatures &&
|
|
||||||
isPublicInternetAccessAllowed(),
|
|
||||||
);
|
|
||||||
|
|
||||||
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
|
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
|
||||||
isNotebookEnabled,
|
isNotebookEnabled,
|
||||||
@@ -1211,7 +1171,7 @@ export default class Explorer {
|
|||||||
await this.initNotebooks(userContext.databaseAccount);
|
await this.initNotebooks(userContext.databaseAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.authType === AuthType.AAD && userContext.apiType === "SQL" && !isFabricNative()) {
|
if (userContext.authType === AuthType.AAD && userContext.apiType === "SQL") {
|
||||||
const throughputBucketsEnabled = await featureRegistered(userContext.subscriptionId, "ThroughputBucketing");
|
const throughputBucketsEnabled = await featureRegistered(userContext.subscriptionId, "ThroughputBucketing");
|
||||||
updateUserContext({ throughputBucketsEnabled });
|
updateUserContext({ throughputBucketsEnabled });
|
||||||
}
|
}
|
||||||
@@ -1221,7 +1181,6 @@ export default class Explorer {
|
|||||||
|
|
||||||
public async configureCopilot(): Promise<void> {
|
public async configureCopilot(): Promise<void> {
|
||||||
if (
|
if (
|
||||||
!isFeatureSupported(PlatformFeature.Copilot) ||
|
|
||||||
userContext.apiType !== "SQL" ||
|
userContext.apiType !== "SQL" ||
|
||||||
!userContext.subscriptionId ||
|
!userContext.subscriptionId ||
|
||||||
![Environment.Development, Environment.Mpac, Environment.Prod].includes(getEnvironment())
|
![Environment.Development, Environment.Mpac, Environment.Prod].includes(getEnvironment())
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -59,7 +59,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;
|
||||||
@@ -1300,7 +1300,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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { KeyboardAction } from "KeyboardShortcuts";
|
import { KeyboardAction } from "KeyboardShortcuts";
|
||||||
import { isDataplaneRbacSupported } from "Utils/APITypeUtils";
|
import { isDataplaneRbacSupported } from "Utils/APITypeUtils";
|
||||||
import { areAdvancedScriptsSupported, isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
|
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
|
||||||
@@ -18,7 +17,7 @@ import SynapseIcon from "../../../../images/synapse-link.svg";
|
|||||||
import VSCodeIcon from "../../../../images/vscode.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 { configContext, Platform } from "../../../ConfigContext";
|
import { Platform, configContext } from "../../../ConfigContext";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
|
||||||
@@ -64,11 +63,9 @@ export function createStaticCommandBarButtons(
|
|||||||
}
|
}
|
||||||
if (userContext.apiType !== "Gremlin") {
|
if (userContext.apiType !== "Gremlin") {
|
||||||
const addVsCode = createOpenVsCodeDialogButton(container);
|
const addVsCode = createOpenVsCodeDialogButton(container);
|
||||||
if (addVsCode) {
|
|
||||||
buttons.push(addVsCode);
|
buttons.push(addVsCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isDataplaneRbacSupported(userContext.apiType)) {
|
if (isDataplaneRbacSupported(userContext.apiType)) {
|
||||||
const [loginButtonProps, setLoginButtonProps] = useState<CommandButtonComponentProps | undefined>(undefined);
|
const [loginButtonProps, setLoginButtonProps] = useState<CommandButtonComponentProps | undefined>(undefined);
|
||||||
@@ -245,17 +242,11 @@ export function createDivider(): CommandButtonComponentProps {
|
|||||||
|
|
||||||
function areScriptsSupported(): boolean {
|
function areScriptsSupported(): boolean {
|
||||||
return (
|
return (
|
||||||
areAdvancedScriptsSupported() &&
|
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
||||||
configContext.platform !== Platform.Fabric &&
|
|
||||||
(userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
|
function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
|
||||||
if (!isFeatureSupported(PlatformFeature.SynapseLink)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configContext.platform === Platform.Emulator) {
|
if (configContext.platform === Platform.Emulator) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -283,10 +274,6 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createOpenVsCodeDialogButton(container: Explorer): CommandButtonComponentProps {
|
function createOpenVsCodeDialogButton(container: Explorer): CommandButtonComponentProps {
|
||||||
if (!isFeatureSupported(PlatformFeature.VSCodeIntegration)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = "Visual Studio Code";
|
const label = "Visual Studio Code";
|
||||||
return {
|
return {
|
||||||
iconSrc: VSCodeIcon,
|
iconSrc: VSCodeIcon,
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -50,10 +50,8 @@ 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, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||||
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
|
|
||||||
import { getUpsellMessage } from "Utils/PricingUtils";
|
import { getUpsellMessage } from "Utils/PricingUtils";
|
||||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
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 { ContainerSampleGenerator } from "../../DataSamples/ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "../../DataSamples/ContainerSampleGenerator";
|
||||||
@@ -62,6 +60,7 @@ import { useDatabases } from "../../useDatabases";
|
|||||||
import { PanelFooterComponent } from "../PanelFooterComponent";
|
import { PanelFooterComponent } from "../PanelFooterComponent";
|
||||||
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
|
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
|
||||||
import { PanelLoadingScreen } from "../PanelLoadingScreen";
|
import { PanelLoadingScreen } from "../PanelLoadingScreen";
|
||||||
|
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||||
|
|
||||||
export interface AddCollectionPanelProps {
|
export interface AddCollectionPanelProps {
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
@@ -124,7 +123,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,
|
||||||
@@ -337,6 +336,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
aria-label="New database id, Type a new database id"
|
aria-label="New database id, Type a new database id"
|
||||||
|
autoFocus
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
value={this.state.newDatabaseId}
|
value={this.state.newDatabaseId}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
@@ -407,9 +407,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
responsiveMode={999}
|
responsiveMode={999}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Separator className="panelSeparator" style={{ marginTop: -4, marginBottom: -4 }} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
<Separator className="panelSeparator" style={{ marginTop: -4, marginBottom: -4 }} />
|
||||||
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal style={{ marginTop: -5, marginBottom: 1 }}>
|
<Stack horizontal style={{ marginTop: -5, marginBottom: 1 }}>
|
||||||
@@ -449,9 +449,8 @@ 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>
|
||||||
|
<Separator className="panelSeparator" style={{ marginTop: -5, marginBottom: -5 }} />
|
||||||
{this.shouldShowIndexingOptionsForFreeTierAccount() && (
|
{this.shouldShowIndexingOptionsForFreeTierAccount() && (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal style={{ marginTop: -4, marginBottom: -5 }}>
|
<Stack horizontal style={{ marginTop: -4, marginBottom: -5 }}>
|
||||||
@@ -646,7 +645,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 } }}
|
||||||
@@ -710,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}
|
||||||
@@ -743,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) => {
|
||||||
@@ -777,9 +777,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isFabricNative() && userContext.apiType === "SQL" && (
|
|
||||||
<Separator className="panelSeparator" style={{ marginTop: -15, marginBottom: -4 }} />
|
<Separator className="panelSeparator" style={{ marginTop: -15, marginBottom: -4 }} />
|
||||||
)}
|
|
||||||
|
|
||||||
{shouldShowAnalyticalStoreOptions() && (
|
{shouldShowAnalyticalStoreOptions() && (
|
||||||
<Stack className="panelGroupSpacing" style={{ marginTop: -4 }}>
|
<Stack className="panelGroupSpacing" style={{ marginTop: -4 }}>
|
||||||
@@ -1135,7 +1133,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
private shouldShowCollectionThroughputInput(): boolean {
|
private shouldShowCollectionThroughputInput(): boolean {
|
||||||
if (isServerlessAccount()) {
|
if (isFabricNative() || isServerlessAccount()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1161,15 +1159,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowVectorSearchParameters() {
|
private shouldShowVectorSearchParameters() {
|
||||||
return (
|
return isVectorSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
|
||||||
isFeatureSupported(PlatformFeature.VectorSearch) &&
|
|
||||||
isVectorSearchEnabled() &&
|
|
||||||
(isServerlessAccount() || this.shouldShowCollectionThroughputInput())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowFullTextSearchParameters() {
|
private shouldShowFullTextSearchParameters() {
|
||||||
return isFeatureSupported(PlatformFeature.FullTextSearch) && !isFabricNative() && this.showFullTextSearch;
|
return !isFabricNative() && this.showFullTextSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
|
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
|
||||||
@@ -1360,8 +1354,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
|
|
||||||
// Throughput
|
// Throughput
|
||||||
if (isFabricNative()) {
|
if (isFabricNative()) {
|
||||||
// Fabric Native accounts are always autoscale and have a fixed throughput of 5K
|
// Fabric Native accounts are always autoscale and have a fixed throughput of 1K
|
||||||
autoPilotMaxThroughput = AutoPilotUtils.autoPilotThroughput5K;
|
autoPilotMaxThroughput = AutoPilotUtils.autoPilotThroughput1K;
|
||||||
offerThroughput = undefined;
|
offerThroughput = undefined;
|
||||||
} else if (databaseLevelThroughput) {
|
} else if (databaseLevelThroughput) {
|
||||||
if (this.state.createNewDatabase) {
|
if (this.state.createNewDatabase) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { getFullTextLanguageOptions } from "Explorer/Controls/FullTextSeach/Full
|
|||||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
|
|
||||||
|
|
||||||
export function getPartitionKeyTooltipText(): string {
|
export function getPartitionKeyTooltipText(): string {
|
||||||
if (userContext.apiType === "Mongo") {
|
if (userContext.apiType === "Mongo") {
|
||||||
@@ -86,11 +85,7 @@ export function UniqueKeysHeader(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function shouldShowAnalyticalStoreOptions(): boolean {
|
export function shouldShowAnalyticalStoreOptions(): boolean {
|
||||||
if (
|
if (isFabricNative() || configContext.platform === Platform.Emulator) {
|
||||||
!isFeatureSupported(PlatformFeature.AnalyticalStore) ||
|
|
||||||
isFabricNative() ||
|
|
||||||
configContext.platform === Platform.Emulator
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
aria-label="New database id, Type a new database id"
|
aria-label="New database id, Type a new database id"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
autoFocus={true}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
id="newDatabaseId"
|
id="newDatabaseId"
|
||||||
name="newDatabaseId"
|
name="newDatabaseId"
|
||||||
@@ -142,6 +143,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
</StyledTooltipHostBase>
|
</StyledTooltipHostBase>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Stack>
|
||||||
<Separator
|
<Separator
|
||||||
className="panelSeparator"
|
className="panelSeparator"
|
||||||
style={
|
style={
|
||||||
@@ -151,7 +153,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
@@ -202,6 +203,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
|
</Stack>
|
||||||
<Separator
|
<Separator
|
||||||
className="panelSeparator"
|
className="panelSeparator"
|
||||||
style={
|
style={
|
||||||
@@ -211,7 +213,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack
|
<Stack
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
|
|||||||
@@ -11,10 +11,10 @@
|
|||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
&> :not(.collapsibleSection) {
|
& > :not(.collapsibleSection) {
|
||||||
margin-bottom: @DefaultSpace;
|
margin-bottom: @DefaultSpace;
|
||||||
|
|
||||||
&> :not(:last-child) {
|
& > :not(:last-child) {
|
||||||
margin-bottom: @DefaultSpace;
|
margin-bottom: @DefaultSpace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,14 +56,6 @@
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.panelMainContent {
|
|
||||||
padding: 0 24px;
|
|
||||||
margin: 0;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelHeader {
|
.panelHeader {
|
||||||
@@ -121,87 +113,70 @@
|
|||||||
.deleteCollectionFeedback {
|
.deleteCollectionFeedback {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addRemoveIcon {
|
.addRemoveIcon {
|
||||||
margin-left: 4px !important;
|
margin-left: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addRemoveIconLabel {
|
.addRemoveIconLabel {
|
||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
margin-left: 4px !important;
|
margin-left: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addRemoveIcon [alt="editEntity"]:focus,
|
.addRemoveIcon [alt="editEntity"]:focus,
|
||||||
.addRemoveIconLabel [alt="editEntity"]:focus {
|
.addRemoveIconLabel [alt="editEntity"]:focus {
|
||||||
border: 1px dashed #605e5c;
|
border: 1px dashed #605e5c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addNewParamStyle {
|
.addNewParamStyle {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-left: 5px !important;
|
margin-left: 5px !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelGroupSpacing> :not(:last-child) {
|
.panelGroupSpacing > :not(:last-child) {
|
||||||
margin-bottom: @DefaultSpace;
|
margin-bottom: @DefaultSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileUpload {
|
.fileUpload {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customFileUpload {
|
.customFileUpload {
|
||||||
padding: 25px 0px 0px 10px;
|
padding: 25px 0px 0px 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileIcon {
|
.fileIcon {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelAddIconLabel {
|
.panelAddIconLabel {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
margin: 30px 0 0 10px;
|
margin: 30px 0 0 10px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelAddIcon {
|
.panelAddIcon {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
margin: 30px 0 0 10px;
|
margin: 30px 0 0 10px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.removeIcon {
|
.removeIcon {
|
||||||
color: @InfoIconColor;
|
color: @InfoIconColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.backImageIcon {
|
.backImageIcon {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[alt="back"]:focus {
|
[alt="back"]:focus {
|
||||||
border: 1px solid #605e5c;
|
border: 1px solid #605e5c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addEntityDatePicker {
|
.addEntityDatePicker {
|
||||||
max-width: 145px;
|
max-width: 145px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addEntityTextField {
|
.addEntityTextField {
|
||||||
width: 237px;
|
width: 237px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addButtonEntiy {
|
.addButtonEntiy {
|
||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-select-view {
|
.column-select-view {
|
||||||
margin: 20px 0px 0px 0px;
|
margin: 20px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panelSeparator::before {
|
.panelSeparator::before {
|
||||||
background-color: #edebe9;
|
background-color: #edebe9;
|
||||||
}
|
}
|
||||||
@@ -433,6 +433,9 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logConsoleInfo(
|
||||||
|
`Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`,
|
||||||
|
);
|
||||||
refreshExplorer && (await explorer.refreshExplorer());
|
refreshExplorer && (await explorer.refreshExplorer());
|
||||||
closeSidePanel();
|
closeSidePanel();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,26 +44,32 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter
|
|||||||
|
|
||||||
resolvedRegion = determineCloudShellRegion();
|
resolvedRegion = determineCloudShellRegion();
|
||||||
|
|
||||||
|
resolvedRegion = determineCloudShellRegion();
|
||||||
|
|
||||||
terminal.writeln(formatWarningMessage("⚠️ IMPORTANT: Azure Cloud Shell Region Notice ⚠️"));
|
terminal.writeln(formatWarningMessage("⚠️ IMPORTANT: Azure Cloud Shell Region Notice ⚠️"));
|
||||||
terminal.writeln(
|
terminal.writeln(
|
||||||
formatInfoMessage(
|
formatInfoMessage(
|
||||||
"The Cloud Shell environment will operate in a region that may differ from your database's region.",
|
"The Cloud Shell environment will operate in a region that may differ from your database's region.",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
terminal.writeln(formatInfoMessage("By using this feature, you acknowledge and agree to the following"));
|
terminal.writeln(formatInfoMessage("This has two potential implications:"));
|
||||||
terminal.writeln(formatInfoMessage("1. Performance Impact:"));
|
terminal.writeln(formatInfoMessage("1. Performance Impact:"));
|
||||||
terminal.writeln(
|
terminal.writeln(
|
||||||
formatInfoMessage(" Commands may experience higher latency due to geographic distance between regions."),
|
formatInfoMessage(" Commands may experience higher latency due to geographic distance between regions."),
|
||||||
);
|
);
|
||||||
terminal.writeln(formatInfoMessage("2. Data Transfers:"));
|
terminal.writeln(formatInfoMessage("2. Data Compliance Considerations:"));
|
||||||
terminal.writeln(
|
terminal.writeln(
|
||||||
formatInfoMessage(
|
formatInfoMessage(
|
||||||
" Data processed through this Cloud Shell service can be processed outside of your tenant's geographical region, compliance boundary or national cloud instance.",
|
" Data processed through this shell could temporarily reside in a different geographic region,",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
terminal.writeln(
|
||||||
|
formatInfoMessage(" which may affect compliance with data residency requirements or regulations specific"),
|
||||||
|
);
|
||||||
|
terminal.writeln(formatInfoMessage(" to your organization."));
|
||||||
terminal.writeln("");
|
terminal.writeln("");
|
||||||
|
|
||||||
terminal.writeln("\x1b[94mFor more information on Azure Cosmos DB data residency, please visit:");
|
terminal.writeln("\x1b[94mFor more information on Azure Cosmos DB data governance and compliance, please visit:");
|
||||||
terminal.writeln("\x1b[94mhttps://learn.microsoft.com/en-us/azure/cosmos-db/data-residency\x1b[0m");
|
terminal.writeln("\x1b[94mhttps://learn.microsoft.com/en-us/azure/cosmos-db/data-residency\x1b[0m");
|
||||||
|
|
||||||
// Ask for user consent for region
|
// Ask for user consent for region
|
||||||
|
|||||||
@@ -258,7 +258,14 @@ Key limitations:
|
|||||||
|
|
||||||
### Data Residency
|
### Data Residency
|
||||||
|
|
||||||
Data residency requirements may not be fully satisfied when using CloudShell due to limited regional availability.
|
Data residency requirements may not be fully satisfied when using CloudShell due to limited regional availability. CloudShell services are currently available in the following regions:
|
||||||
|
|
||||||
|
| Geography | Regions |
|
||||||
|
|-----------|---------|
|
||||||
|
| Americas | East US, West US 2, South Central US, West Central US |
|
||||||
|
| Europe | West Europe, North Europe |
|
||||||
|
| Asia Pacific | Southeast Asia, Japan East, Australia East |
|
||||||
|
| Middle East | UAE North |
|
||||||
|
|
||||||
**Note:** For up-to-date supported regions, refer to the region configuration in:
|
**Note:** For up-to-date supported regions, refer to the region configuration in:
|
||||||
`src/Explorer/CloudShell/Configuration/RegionConfig.ts`
|
`src/Explorer/CloudShell/Configuration/RegionConfig.ts`
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AbstractShellHandler, DISABLE_HISTORY, EXIT_COMMAND, START_MARKER } from "./AbstractShellHandler";
|
import { AbstractShellHandler, DISABLE_HISTORY, START_MARKER, EXIT_COMMAND } from "./AbstractShellHandler";
|
||||||
|
|
||||||
// Mock implementation for testing
|
// Mock implementation for testing
|
||||||
class MockShellHandler extends AbstractShellHandler {
|
class MockShellHandler extends AbstractShellHandler {
|
||||||
@@ -18,8 +18,8 @@ class MockShellHandler extends AbstractShellHandler {
|
|||||||
return "mock-endpoint";
|
return "mock-endpoint";
|
||||||
}
|
}
|
||||||
|
|
||||||
getTerminalSuppressedData(): string[] {
|
getTerminalSuppressedData(): string {
|
||||||
return ["suppressed-data"];
|
return "suppressed-data";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ describe("AbstractShellHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the terminal suppressed data", () => {
|
it("should return the terminal suppressed data", () => {
|
||||||
expect(shellHandler.getTerminalSuppressedData()).toEqual(["suppressed-data"]);
|
expect(shellHandler.getTerminalSuppressedData()).toBe("suppressed-data");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,13 +13,7 @@ export const DISABLE_HISTORY = `set +o history`;
|
|||||||
* Command that displays an error message and exits the shell session.
|
* Command that displays an error message and exits the shell session.
|
||||||
* Used when shell initialization or connection fails.
|
* Used when shell initialization or connection fails.
|
||||||
*/
|
*/
|
||||||
export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close this tab and initiate a new shell session if needed.\\033[0m\\n" && disown -a && exit`;
|
export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close this tab and initiate a new shell session if needed.\\033[0m\\n" && exit`;
|
||||||
|
|
||||||
/**
|
|
||||||
* This command runs mongosh in no-database and quiet mode,
|
|
||||||
* and evaluates the `disableTelemetry()` function to turn off telemetry collection.
|
|
||||||
*/
|
|
||||||
export const DISABLE_TELEMETRY_COMMAND = `mongosh --nodb --quiet --eval "disableTelemetry()"`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class that defines the interface for shell-specific handlers
|
* Abstract class that defines the interface for shell-specific handlers
|
||||||
@@ -37,8 +31,7 @@ export abstract class AbstractShellHandler {
|
|||||||
abstract getShellName(): string;
|
abstract getShellName(): string;
|
||||||
abstract getSetUpCommands(): string[];
|
abstract getSetUpCommands(): string[];
|
||||||
abstract getConnectionCommand(): string;
|
abstract getConnectionCommand(): string;
|
||||||
abstract getTerminalSuppressedData(): string[];
|
abstract getTerminalSuppressedData(): string;
|
||||||
updateTerminalData?(data: string): string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the complete initialization command sequence for the shell.
|
* Constructs the complete initialization command sequence for the shell.
|
||||||
@@ -84,7 +77,7 @@ export abstract class AbstractShellHandler {
|
|||||||
* is not already present in the environment.
|
* is not already present in the environment.
|
||||||
*/
|
*/
|
||||||
protected mongoShellSetupCommands(): string[] {
|
protected mongoShellSetupCommands(): string[] {
|
||||||
const PACKAGE_VERSION: string = "2.5.5";
|
const PACKAGE_VERSION: string = "2.5.0";
|
||||||
return [
|
return [
|
||||||
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
|
"if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi",
|
||||||
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
`if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
||||||
@@ -92,7 +85,7 @@ export abstract class AbstractShellHandler {
|
|||||||
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh/bin && mv mongosh-${PACKAGE_VERSION}-linux-x64/bin/mongosh ~/mongosh/bin/ && chmod +x ~/mongosh/bin/mongosh; fi`,
|
`if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh/bin && mv mongosh-${PACKAGE_VERSION}-linux-x64/bin/mongosh ~/mongosh/bin/ && chmod +x ~/mongosh/bin/mongosh; fi`,
|
||||||
`if ! command -v mongosh &> /dev/null; then rm -rf mongosh-${PACKAGE_VERSION}-linux-x64 mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
`if ! command -v mongosh &> /dev/null; then rm -rf mongosh-${PACKAGE_VERSION}-linux-x64 mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`,
|
||||||
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
|
"if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi",
|
||||||
"if ! command -v mongosh &> /dev/null; then source ~/.bashrc; fi",
|
"source ~/.bashrc",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ describe("CassandraShellHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should return the correct terminal suppressed data", () => {
|
test("should return the correct terminal suppressed data", () => {
|
||||||
expect(handler.getTerminalSuppressedData()).toEqual([""]);
|
expect(handler.getTerminalSuppressedData()).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should include the correct package version in setup commands", () => {
|
test("should include the correct package version in setup commands", () => {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export class CassandraShellHandler extends AbstractShellHandler {
|
|||||||
return `cqlsh ${getHostFromUrl(this._endpoint)} 10350 -u ${dbName} -p ${this._key} --ssl`;
|
return `cqlsh ${getHostFromUrl(this._endpoint)} 10350 -u ${dbName} -p ${this._key} --ssl`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string[] {
|
public getTerminalSuppressedData(): string {
|
||||||
return [""];
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ describe("MongoShellHandler", () => {
|
|||||||
|
|
||||||
expect(Array.isArray(commands)).toBe(true);
|
expect(Array.isArray(commands)).toBe(true);
|
||||||
expect(commands.length).toBe(7);
|
expect(commands.length).toBe(7);
|
||||||
expect(commands[1]).toContain("mongosh-2.5.5-linux-x64.tgz");
|
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ describe("MongoShellHandler", () => {
|
|||||||
const command = mongoShellHandler.getConnectionCommand();
|
const command = mongoShellHandler.getConnectionCommand();
|
||||||
|
|
||||||
expect(command).toBe(
|
expect(command).toBe(
|
||||||
'mongosh --nodb --quiet --eval "disableTelemetry()" && mongosh mongodb://test-mongo.documents.azure.com:10255?appName=CosmosExplorerTerminal --username test-account --password test-key --tls --tlsAllowInvalidCertificates',
|
"mongosh mongodb://test-mongo.documents.azure.com:10255?appName=CosmosExplorerTerminal --username test-account --password test-key --tls --tlsAllowInvalidCertificates",
|
||||||
);
|
);
|
||||||
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-mongo.documents.azure.com:443/");
|
expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-mongo.documents.azure.com:443/");
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ describe("MongoShellHandler", () => {
|
|||||||
|
|
||||||
describe("getTerminalSuppressedData", () => {
|
describe("getTerminalSuppressedData", () => {
|
||||||
it("should return the correct warning message", () => {
|
it("should return the correct warning message", () => {
|
||||||
expect(mongoShellHandler.getTerminalSuppressedData()).toEqual(["Warning: Non-Genuine MongoDB Detected"]);
|
expect(mongoShellHandler.getTerminalSuppressedData()).toBe("Warning: Non-Genuine MongoDB Detected");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { userContext } from "../../../../UserContext";
|
import { userContext } from "../../../../UserContext";
|
||||||
import { getHostFromUrl } from "../Utils/CommonUtils";
|
import { getHostFromUrl } from "../Utils/CommonUtils";
|
||||||
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND } from "./AbstractShellHandler";
|
import { AbstractShellHandler } from "./AbstractShellHandler";
|
||||||
|
|
||||||
export class MongoShellHandler extends AbstractShellHandler {
|
export class MongoShellHandler extends AbstractShellHandler {
|
||||||
private _key: string;
|
private _key: string;
|
||||||
@@ -29,8 +29,6 @@ export class MongoShellHandler extends AbstractShellHandler {
|
|||||||
return "echo 'Database name not found.'";
|
return "echo 'Database name not found.'";
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
DISABLE_TELEMETRY_COMMAND +
|
|
||||||
" && " +
|
|
||||||
"mongosh mongodb://" +
|
"mongosh mongodb://" +
|
||||||
getHostFromUrl(this._endpoint) +
|
getHostFromUrl(this._endpoint) +
|
||||||
":10255?appName=" +
|
":10255?appName=" +
|
||||||
@@ -43,7 +41,7 @@ export class MongoShellHandler extends AbstractShellHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string[] {
|
public getTerminalSuppressedData(): string {
|
||||||
return ["Warning: Non-Genuine MongoDB Detected"];
|
return "Warning: Non-Genuine MongoDB Detected";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ describe("PostgresShellHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return empty string for terminal suppressed data", () => {
|
it("should return empty string for terminal suppressed data", () => {
|
||||||
expect(postgresShellHandler.getTerminalSuppressedData()).toEqual([""]);
|
expect(postgresShellHandler.getTerminalSuppressedData()).toBe("");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class PostgresShellHandler extends AbstractShellHandler {
|
|||||||
return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require --set=application_name=${this.APP_NAME}`;
|
return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require --set=application_name=${this.APP_NAME}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string[] {
|
public getTerminalSuppressedData(): string {
|
||||||
return [""];
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ describe("VCoreMongoShellHandler", () => {
|
|||||||
|
|
||||||
expect(Array.isArray(commands)).toBe(true);
|
expect(Array.isArray(commands)).toBe(true);
|
||||||
expect(commands.length).toBe(7);
|
expect(commands.length).toBe(7);
|
||||||
expect(commands[1]).toContain("mongosh-2.5.5-linux-x64.tgz");
|
expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz");
|
||||||
expect(commands[0]).toContain("mongosh not found");
|
expect(commands[0]).toContain("mongosh not found");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,10 +57,7 @@ describe("VCoreMongoShellHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the correct terminal suppressed data", () => {
|
it("should return the correct terminal suppressed data", () => {
|
||||||
expect(vcoreMongoShellHandler.getTerminalSuppressedData()).toEqual([
|
expect(vcoreMongoShellHandler.getTerminalSuppressedData()).toBe("Warning: Non-Genuine MongoDB Detected");
|
||||||
"Warning: Non-Genuine MongoDB Detected",
|
|
||||||
"Telemetry is now disabled.",
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import { userContext } from "../../../../UserContext";
|
import { userContext } from "../../../../UserContext";
|
||||||
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND } from "./AbstractShellHandler";
|
import { AbstractShellHandler } from "./AbstractShellHandler";
|
||||||
|
|
||||||
export class VCoreMongoShellHandler extends AbstractShellHandler {
|
export class VCoreMongoShellHandler extends AbstractShellHandler {
|
||||||
private _endpoint: string | undefined;
|
private _endpoint: string | undefined;
|
||||||
private _textFilterRules: string[] = [
|
|
||||||
"For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/",
|
|
||||||
"disableTelemetry() command",
|
|
||||||
"https://www.mongodb.com/legal/privacy-policy",
|
|
||||||
];
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -28,22 +23,10 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userName = userContext.vcoreMongoConnectionParams.adminLogin;
|
const userName = userContext.vcoreMongoConnectionParams.adminLogin;
|
||||||
|
return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000&appName=${this.APP_NAME}"`;
|
||||||
const connectionUri = `mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000&appName=${this.APP_NAME}`;
|
|
||||||
|
|
||||||
return `${DISABLE_TELEMETRY_COMMAND} && mongosh "${connectionUri}"`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTerminalSuppressedData(): string[] {
|
public getTerminalSuppressedData(): string {
|
||||||
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
|
return "Warning: Non-Genuine MongoDB Detected";
|
||||||
}
|
|
||||||
|
|
||||||
updateTerminalData(content: string): string {
|
|
||||||
const updatedContent = content
|
|
||||||
.split("\n")
|
|
||||||
.filter((line) => !this._textFilterRules.some((part) => line.includes(part)))
|
|
||||||
.filter((line, idx, arr) => (arr.length > 3 && idx <= arr.length - 3 ? !["", "\r"].includes(line) : true)) // Filter out empty lines and carriage returns, but keep the last 3 lines if they exist
|
|
||||||
.join("\n");
|
|
||||||
return updatedContent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,13 +135,11 @@ export class AttachAddon implements ITerminalAddon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this._allowTerminalWrite) {
|
if (this._allowTerminalWrite) {
|
||||||
const updatedData = this._shellHandler?.updateTerminalData(data) ?? data;
|
|
||||||
const suppressedData = this._shellHandler?.getTerminalSuppressedData();
|
const suppressedData = this._shellHandler?.getTerminalSuppressedData();
|
||||||
|
const hasSuppressedData = suppressedData && suppressedData.length > 0;
|
||||||
|
|
||||||
const shouldNotWrite = suppressedData.filter(Boolean).some((item) => updatedData.includes(item));
|
if (!hasSuppressedData || !data.includes(suppressedData)) {
|
||||||
|
terminal.write(data);
|
||||||
if (!shouldNotWrite) {
|
|
||||||
terminal.write(updatedData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export interface IGraphConfig {
|
|||||||
|
|
||||||
interface GraphTabOptions extends ViewModels.TabOptions {
|
interface GraphTabOptions extends ViewModels.TabOptions {
|
||||||
account: DatabaseAccount;
|
account: DatabaseAccount;
|
||||||
password: string;
|
masterKey: string;
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
collectionPartitionKeyProperty: string;
|
collectionPartitionKeyProperty: string;
|
||||||
@@ -107,7 +107,7 @@ export default class GraphTab extends TabsBase {
|
|||||||
graphBackendEndpoint: GraphTab.getGremlinEndpoint(options.account),
|
graphBackendEndpoint: GraphTab.getGremlinEndpoint(options.account),
|
||||||
databaseId: options.databaseId,
|
databaseId: options.databaseId,
|
||||||
collectionId: options.collectionId,
|
collectionId: options.collectionId,
|
||||||
password: options.password,
|
masterKey: options.masterKey,
|
||||||
onLoadStartKey: options.onLoadStartKey,
|
onLoadStartKey: options.onLoadStartKey,
|
||||||
onLoadStartKeyChange: (onLoadStartKey: number): void => {
|
onLoadStartKeyChange: (onLoadStartKey: number): void => {
|
||||||
if (onLoadStartKey === undefined) {
|
if (onLoadStartKey === undefined) {
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPa
|
|||||||
import { SaveQueryPane } from "../../Panes/SaveQueryPane/SaveQueryPane";
|
import { SaveQueryPane } from "../../Panes/SaveQueryPane/SaveQueryPane";
|
||||||
import TabsBase from "../TabsBase";
|
import TabsBase from "../TabsBase";
|
||||||
import "./QueryTabComponent.less";
|
import "./QueryTabComponent.less";
|
||||||
|
import { useQueryMetadataStore } from "./useQueryMetadataStore"; // adjust path if needed
|
||||||
|
|
||||||
|
|
||||||
enum ToggleState {
|
enum ToggleState {
|
||||||
Result,
|
Result,
|
||||||
@@ -196,6 +198,9 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
|||||||
enabled: !!this.state.sqlQueryEditorContent && this.state.sqlQueryEditorContent.length > 0,
|
enabled: !!this.state.sqlQueryEditorContent && this.state.sqlQueryEditorContent.length > 0,
|
||||||
visible: true,
|
visible: true,
|
||||||
};
|
};
|
||||||
|
// const query=this.state.sqlQueryEditorContent;
|
||||||
|
// const db = this.props.collection.databaseId;
|
||||||
|
// const container = this.props.collection.id();
|
||||||
|
|
||||||
const isSaveQueryBtnEnabled = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
const isSaveQueryBtnEnabled = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
|
||||||
this.saveQueryButton = {
|
this.saveQueryButton = {
|
||||||
@@ -260,6 +265,10 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onExecuteQueryClick = async (): Promise<void> => {
|
public onExecuteQueryClick = async (): Promise<void> => {
|
||||||
|
const query1=this.state.sqlQueryEditorContent;
|
||||||
|
const db = this.props.collection.databaseId;
|
||||||
|
const container = this.props.collection.id();
|
||||||
|
useQueryMetadataStore.getState().setMetadata(query1, db, container);
|
||||||
this._iterator = undefined;
|
this._iterator = undefined;
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { FontIcon } from "@fluentui/react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
DataGrid,
|
DataGrid,
|
||||||
DataGridBody,
|
DataGridBody,
|
||||||
DataGridCell,
|
DataGridCell,
|
||||||
@@ -8,26 +10,36 @@ import {
|
|||||||
DataGridRow,
|
DataGridRow,
|
||||||
SelectTabData,
|
SelectTabData,
|
||||||
SelectTabEvent,
|
SelectTabEvent,
|
||||||
|
Spinner,
|
||||||
Tab,
|
Tab,
|
||||||
TabList,
|
TabList,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
TableColumnDefinition,
|
TableColumnDefinition,
|
||||||
createTableColumn,
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
createTableColumn
|
||||||
} from "@fluentui/react-components";
|
} from "@fluentui/react-components";
|
||||||
import { ArrowDownloadRegular, CopyRegular } from "@fluentui/react-icons";
|
import { ArrowDownloadRegular, ChevronDown20Regular, ChevronRight20Regular, CircleFilled, CopyRegular } from "@fluentui/react-icons";
|
||||||
|
import copy from "clipboard-copy";
|
||||||
import { HttpHeaders } from "Common/Constants";
|
import { HttpHeaders } from "Common/Constants";
|
||||||
import MongoUtility from "Common/MongoUtility";
|
import MongoUtility from "Common/MongoUtility";
|
||||||
import { QueryMetrics } from "Contracts/DataModels";
|
import { QueryMetrics } from "Contracts/DataModels";
|
||||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||||
import { IDocument } from "Explorer/Tabs/QueryTab/QueryTabComponent";
|
import { IDocument } from "Explorer/Tabs/QueryTab/QueryTabComponent";
|
||||||
import { useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles";
|
import { useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles";
|
||||||
|
import { useQueryMetadataStore } from "Explorer/Tabs/QueryTab/useQueryMetadataStore";
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import copy from "clipboard-copy";
|
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||||
import React, { useCallback, useState } from "react";
|
import { client } from "../../../Common/CosmosClient";
|
||||||
|
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
import { ResultsViewProps } from "./QueryResultSection";
|
import { ResultsViewProps } from "./QueryResultSection";
|
||||||
|
|
||||||
enum ResultsTabs {
|
enum ResultsTabs {
|
||||||
Results = "results",
|
Results = "results",
|
||||||
QueryStats = "queryStats",
|
QueryStats = "queryStats",
|
||||||
|
IndexAdvisor = "indexadv",
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, executeQueryDocumentsPage }) => {
|
const ResultsTab: React.FC<ResultsViewProps> = ({ queryResults, isMongoDB, executeQueryDocumentsPage }) => {
|
||||||
@@ -380,8 +392,7 @@ const QueryStatsTab: React.FC<Pick<ResultsViewProps, "queryResults">> = ({ query
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
metric: "User defined function execution time",
|
metric: "User defined function execution time",
|
||||||
value: `${
|
value: `${aggregatedQueryMetrics.runtimeExecutionTimes?.userDefinedFunctionExecutionTime?.toString() || 0
|
||||||
aggregatedQueryMetrics.runtimeExecutionTimes?.userDefinedFunctionExecutionTime?.toString() || 0
|
|
||||||
} ms`,
|
} ms`,
|
||||||
toolTip: "Total time spent executing user-defined functions",
|
toolTip: "Total time spent executing user-defined functions",
|
||||||
},
|
},
|
||||||
@@ -523,6 +534,394 @@ const QueryStatsTab: React.FC<Pick<ResultsViewProps, "queryResults">> = ({ query
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface IIndexMetric {
|
||||||
|
index: string;
|
||||||
|
impact: string;
|
||||||
|
section: "Included" | "Not Included" | "Header";
|
||||||
|
}
|
||||||
|
const IndexAdvisorTab: React.FC = () => {
|
||||||
|
const { userQuery, databaseId, containerId } = useQueryMetadataStore();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [indexMetrics, setIndexMetrics] = useState<any>(null);
|
||||||
|
const [showIncluded, setShowIncluded] = useState(true);
|
||||||
|
const [showNotIncluded, setShowNotIncluded] = useState(true);
|
||||||
|
const [selectedIndexes, setSelectedIndexes] = useState<any[]>([]);
|
||||||
|
const [selectAll, setSelectAll] = useState(false);
|
||||||
|
const [updateMessageShown, setUpdateMessageShown] = useState(false);
|
||||||
|
const [included, setIncludedIndexes] = useState<IIndexMetric[]>([]);
|
||||||
|
const [notIncluded, setNotIncludedIndexes] = useState<IIndexMetric[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchIndexMetrics() {
|
||||||
|
const clearMessage = logConsoleProgress(`Querying items with IndexMetrics in container ${containerId}`);
|
||||||
|
try {
|
||||||
|
const querySpec = {
|
||||||
|
query: userQuery || "SELECT TOP 10 c.id FROM c WHERE c.Item = 'value1234' ",
|
||||||
|
};
|
||||||
|
const sdkResponse = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(containerId)
|
||||||
|
.items.query(querySpec, {
|
||||||
|
populateIndexMetrics: true,
|
||||||
|
})
|
||||||
|
.fetchAll();
|
||||||
|
setIndexMetrics(sdkResponse.indexMetrics);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "queryItemsWithIndexMetrics", `Error querying items from ${containerId}`);
|
||||||
|
} finally {
|
||||||
|
clearMessage();
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (userQuery && databaseId && containerId) {
|
||||||
|
fetchIndexMetrics();
|
||||||
|
}
|
||||||
|
}, [userQuery, databaseId, containerId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!indexMetrics) return;
|
||||||
|
|
||||||
|
const included: any[] = [];
|
||||||
|
const notIncluded: any[] = [];
|
||||||
|
const lines = indexMetrics.split("\n").map((line: string) => line.trim()).filter(Boolean);
|
||||||
|
let currentSection = "";
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
if (line.startsWith("Utilized Single Indexes") || line.startsWith("Utilized Composite Indexes")) {
|
||||||
|
currentSection = "included";
|
||||||
|
} else if (line.startsWith("Potential Single Indexes") || line.startsWith("Potential Composite Indexes")) {
|
||||||
|
currentSection = "notIncluded";
|
||||||
|
} else if (line.startsWith("Index Spec:")) {
|
||||||
|
const index = line.replace("Index Spec:", "").trim();
|
||||||
|
const impactLine = lines[i + 1];
|
||||||
|
const impact = impactLine?.includes("Index Impact Score:") ? impactLine.split(":")[1].trim() : "Unknown";
|
||||||
|
|
||||||
|
const isComposite = index.includes(",");
|
||||||
|
const indexObj: any = { index, impact };
|
||||||
|
if (isComposite) {
|
||||||
|
indexObj.composite = index.split(",").map((part: string) => {
|
||||||
|
const [path, order] = part.trim().split(/\s+/);
|
||||||
|
return {
|
||||||
|
path: path.trim(),
|
||||||
|
order: order?.toLowerCase() === "desc" ? "descending" : "ascending",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let path = "/unknown/*";
|
||||||
|
const pathRegex = /\/[^\/\s*?]+(?:\/[^\/\s*?]+)*(\/\*|\?)/;
|
||||||
|
const match = index.match(pathRegex);
|
||||||
|
if (match) {
|
||||||
|
path = match[0];
|
||||||
|
} else {
|
||||||
|
const simplePathRegex = /\/[^\/\s]+/;
|
||||||
|
const simpleMatch = index.match(simplePathRegex);
|
||||||
|
if (simpleMatch) {
|
||||||
|
path = simpleMatch[0] + "/*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indexObj.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSection === "included") {
|
||||||
|
included.push(indexObj);
|
||||||
|
} else if (currentSection === "notIncluded") {
|
||||||
|
notIncluded.push(indexObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIncludedIndexes(included);
|
||||||
|
setNotIncludedIndexes(notIncluded);
|
||||||
|
}, [indexMetrics]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const allSelected = notIncluded.length > 0 && notIncluded.every((item) => selectedIndexes.some((s) => s.index === item.index));
|
||||||
|
setSelectAll(allSelected);
|
||||||
|
}, [selectedIndexes, notIncluded]);
|
||||||
|
|
||||||
|
const handleCheckboxChange = (indexObj: any, checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
setSelectedIndexes((prev) => [...prev, indexObj]);
|
||||||
|
} else {
|
||||||
|
setSelectedIndexes((prev) =>
|
||||||
|
prev.filter((item) => item.index !== indexObj.index)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectAll = (checked: boolean) => {
|
||||||
|
setSelectAll(checked);
|
||||||
|
setSelectedIndexes(checked ? notIncluded : []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdatePolicy = async () => {
|
||||||
|
if (selectedIndexes.length === 0) {
|
||||||
|
console.log("No indexes selected for update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { resource: containerDef } = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(containerId)
|
||||||
|
.read();
|
||||||
|
|
||||||
|
const newIncludedPaths = selectedIndexes
|
||||||
|
.filter(index => !index.composite)
|
||||||
|
.map(index => {
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: index.path,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const newCompositeIndexes = selectedIndexes
|
||||||
|
.filter(index => index.composite)
|
||||||
|
.map(index => index.composite);
|
||||||
|
|
||||||
|
const updatedPolicy = {
|
||||||
|
...containerDef.indexingPolicy,
|
||||||
|
includedPaths: [
|
||||||
|
...(containerDef.indexingPolicy?.includedPaths || []),
|
||||||
|
...newIncludedPaths,
|
||||||
|
],
|
||||||
|
compositeIndexes: [
|
||||||
|
...(containerDef.indexingPolicy?.compositeIndexes || []),
|
||||||
|
...newCompositeIndexes,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(containerId)
|
||||||
|
.replace({
|
||||||
|
id: containerId,
|
||||||
|
partitionKey: containerDef.partitionKey,
|
||||||
|
indexingPolicy: updatedPolicy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newIncluded = [...included, ...notIncluded.filter(item =>
|
||||||
|
selectedIndexes.find(s => s.index === item.index)
|
||||||
|
)];
|
||||||
|
const newNotIncluded = notIncluded.filter(item =>
|
||||||
|
!selectedIndexes.find(s => s.index === item.index)
|
||||||
|
);
|
||||||
|
|
||||||
|
setSelectedIndexes([]);
|
||||||
|
setSelectAll(false);
|
||||||
|
setIndexMetricsFromParsed(newIncluded, newNotIncluded);
|
||||||
|
setUpdateMessageShown(true);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to update indexing policy:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setIndexMetricsFromParsed = (included: { index: string; impact: string }[], notIncluded: { index: string; impact: string }[]) => {
|
||||||
|
const serialize = (sectionTitle: string, items: { index: string; impact: string }[], isUtilized: boolean) =>
|
||||||
|
items.length
|
||||||
|
? `${sectionTitle}\n` +
|
||||||
|
items
|
||||||
|
.map((item) => `Index Spec: ${item.index}\nIndex Impact Score: ${item.impact}`)
|
||||||
|
.join("\n") + "\n"
|
||||||
|
: "";
|
||||||
|
const composedMetrics =
|
||||||
|
serialize("Utilized Single Indexes", included, true) +
|
||||||
|
serialize("Potential Single Indexes", notIncluded, false);
|
||||||
|
|
||||||
|
setIndexMetrics(composedMetrics.trim());
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderImpactDots = (impact: string) => {
|
||||||
|
let count = 0;
|
||||||
|
if (impact === "High") count = 3;
|
||||||
|
else if (impact === "Medium") count = 2;
|
||||||
|
else if (impact === "Low") count = 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|
||||||
|
{Array.from({ length: count }).map((_, i) => (
|
||||||
|
<CircleFilled
|
||||||
|
key={i}
|
||||||
|
style={{
|
||||||
|
color: "#0078D4",
|
||||||
|
fontSize: "12px",
|
||||||
|
display: "inline-flex",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderRow = (item: IIndexMetric, index: number) => {
|
||||||
|
const isHeader = item.section === "Header";
|
||||||
|
const isNotIncluded = item.section === "Not Included";
|
||||||
|
const isIncluded = item.section === "Included";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={index}>
|
||||||
|
<TableCell colSpan={2}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "30px 30px 1fr 50px 120px",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "8px",
|
||||||
|
}}>
|
||||||
|
{isNotIncluded ? (
|
||||||
|
<Checkbox
|
||||||
|
checked={selectedIndexes.some((selected) => selected.index === item.index)}
|
||||||
|
onChange={(_, data) => handleCheckboxChange(item, data.checked === true)}
|
||||||
|
/>
|
||||||
|
) : isHeader && item.index === "Not Included in Current Policy" && notIncluded.length > 0 ? (
|
||||||
|
<Checkbox
|
||||||
|
checked={selectAll}
|
||||||
|
onChange={(_, data) => handleSelectAll(data.checked === true)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ width: "18px", height: "18px" }}></div>
|
||||||
|
)}
|
||||||
|
{isHeader ? (
|
||||||
|
<span
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => {
|
||||||
|
if (item.index === "Included in Current Policy") {
|
||||||
|
setShowIncluded(!showIncluded);
|
||||||
|
} else if (item.index === "Not Included in Current Policy") {
|
||||||
|
setShowNotIncluded(!showNotIncluded);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.index === "Included in Current Policy"
|
||||||
|
? showIncluded
|
||||||
|
? <ChevronDown20Regular />
|
||||||
|
: <ChevronRight20Regular />
|
||||||
|
: showNotIncluded
|
||||||
|
? <ChevronDown20Regular />
|
||||||
|
: <ChevronRight20Regular />
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<div style={{ width: "24px" }}></div>
|
||||||
|
)}
|
||||||
|
<div style={{ fontWeight: isHeader ? "bold" : "normal" }}>
|
||||||
|
{item.index}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: isHeader ? 0 : undefined }}>
|
||||||
|
{isHeader ? null : item.impact}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{isHeader ? null : renderImpactDots(item.impact)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const generateIndexMetricItems = (
|
||||||
|
|
||||||
|
included: { index: string; impact: string }[],
|
||||||
|
notIncluded: { index: string; impact: string }[]
|
||||||
|
): IIndexMetric[] => {
|
||||||
|
const items: IIndexMetric[] = [];
|
||||||
|
|
||||||
|
items.push({ index: "Not Included in Current Policy", impact: "", section: "Header" });
|
||||||
|
if (showNotIncluded) {
|
||||||
|
notIncluded.forEach((item) =>
|
||||||
|
items.push({ ...item, section: "Not Included" })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
items.push({ index: "Included in Current Policy", impact: "", section: "Header" });
|
||||||
|
if (showIncluded) {
|
||||||
|
included.forEach((item) =>
|
||||||
|
items.push({ ...item, section: "Included" })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div>
|
||||||
|
<Spinner
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
'--spinner-size': '16px',
|
||||||
|
'--spinner-thickness': '2px',
|
||||||
|
'--spinner-color': '#0078D4',
|
||||||
|
} as React.CSSProperties} />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{ padding: "1rem", fontSize: "1.2rem", display: "flex", alignItems: "center", gap: "0.5rem" }}>
|
||||||
|
{updateMessageShown ? (
|
||||||
|
<>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
borderRadius: "50%",
|
||||||
|
backgroundColor: "#107C10",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}>
|
||||||
|
<FontIcon iconName="CheckMark" style={{ color: "white", fontSize: 12 }} />
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Your indexing policy has been updated with the new included paths. You may review the changes in Scale & Settings.
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
"Here is an analysis on the indexes utilized for executing the query. Based on the analysis, Cosmos DB recommends adding the selected indexes to your indexing policy to optimize the performance of this particular query."
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div style={{ padding: "1rem", fontSize: "1.3rem", fontWeight: "bold" }}>Indexes analysis</div>
|
||||||
|
<Table style={{ display: "block", alignItems: "center", marginBottom: "7rem" }}>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow >
|
||||||
|
<TableCell colSpan={2}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "30px 30px 1fr 50px 120px",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "8px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ width: "18px", height: "18px" }}></div>
|
||||||
|
<div style={{ width: "24px" }}></div>
|
||||||
|
<div>Index</div>
|
||||||
|
<div><span style={{ whiteSpace: "nowrap" }}>Estimated Impact</span></div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{generateIndexMetricItems(included, notIncluded).map(renderRow)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
{selectedIndexes.length > 0 && (
|
||||||
|
<div style={{ padding: "1rem", marginTop: "-7rem", flexWrap: "wrap" }}>
|
||||||
|
<button
|
||||||
|
onClick={handleUpdatePolicy}
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#0078D4",
|
||||||
|
color: "white",
|
||||||
|
padding: "8px 16px",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: "4px",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginTop: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update Indexing Policy with selected index(es)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
export const ResultsView: React.FC<ResultsViewProps> = ({ isMongoDB, queryResults, executeQueryDocumentsPage }) => {
|
export const ResultsView: React.FC<ResultsViewProps> = ({ isMongoDB, queryResults, executeQueryDocumentsPage }) => {
|
||||||
const styles = useQueryTabStyles();
|
const styles = useQueryTabStyles();
|
||||||
const [activeTab, setActiveTab] = useState<ResultsTabs>(ResultsTabs.Results);
|
const [activeTab, setActiveTab] = useState<ResultsTabs>(ResultsTabs.Results);
|
||||||
@@ -548,6 +947,13 @@ export const ResultsView: React.FC<ResultsViewProps> = ({ isMongoDB, queryResult
|
|||||||
>
|
>
|
||||||
Query Stats
|
Query Stats
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
data-test="QueryTab/ResultsPane/ResultsView/IndexAdvisorTab"
|
||||||
|
id={ResultsTabs.IndexAdvisor}
|
||||||
|
value={ResultsTabs.IndexAdvisor}
|
||||||
|
>
|
||||||
|
Index Advisor
|
||||||
|
</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
<div className={styles.queryResultsTabContentContainer}>
|
<div className={styles.queryResultsTabContentContainer}>
|
||||||
{activeTab === ResultsTabs.Results && (
|
{activeTab === ResultsTabs.Results && (
|
||||||
@@ -558,6 +964,7 @@ export const ResultsView: React.FC<ResultsViewProps> = ({ isMongoDB, queryResult
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activeTab === ResultsTabs.QueryStats && <QueryStatsTab queryResults={queryResults} />}
|
{activeTab === ResultsTabs.QueryStats && <QueryStatsTab queryResults={queryResults} />}
|
||||||
|
{activeTab === ResultsTabs.IndexAdvisor && <IndexAdvisorTab />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
16
src/Explorer/Tabs/QueryTab/useQueryMetadataStore.ts
Normal file
16
src/Explorer/Tabs/QueryTab/useQueryMetadataStore.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import create from "zustand";
|
||||||
|
|
||||||
|
interface QueryMetadataStore {
|
||||||
|
userQuery: string;
|
||||||
|
databaseId: string;
|
||||||
|
containerId: string;
|
||||||
|
setMetadata: (query1: string, db: string, container: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useQueryMetadataStore = create<QueryMetadataStore>((set) => ({
|
||||||
|
userQuery: "",
|
||||||
|
databaseId: "",
|
||||||
|
containerId: "",
|
||||||
|
setMetadata: (query1, db, container) =>
|
||||||
|
set({ userQuery: query1, databaseId: db, containerId: container }),
|
||||||
|
}));
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||||
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
|
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
|
||||||
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
||||||
import { useDataplaneRbacAuthorization } from "Utils/AuthorizationUtils";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
@@ -480,8 +479,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
node: this,
|
node: this,
|
||||||
title: title,
|
title: title,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
password: useDataplaneRbacAuthorization(userContext) ? userContext.aadToken : userContext.masterKey || "",
|
|
||||||
collection: this,
|
collection: this,
|
||||||
|
masterKey: userContext.masterKey || "",
|
||||||
collectionPartitionKeyProperty: this.partitionKeyProperties?.[0],
|
collectionPartitionKeyProperty: this.partitionKeyProperties?.[0],
|
||||||
collectionId: this.id(),
|
collectionId: this.id(),
|
||||||
databaseId: this.databaseId,
|
databaseId: this.databaseId,
|
||||||
@@ -737,7 +737,7 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
title: title,
|
title: title,
|
||||||
tabPath: "",
|
tabPath: "",
|
||||||
collection: this,
|
collection: this,
|
||||||
password: useDataplaneRbacAuthorization(userContext) ? userContext.aadToken : userContext.masterKey || "",
|
masterKey: userContext.masterKey || "",
|
||||||
collectionPartitionKeyProperty: this.partitionKeyProperties?.[0],
|
collectionPartitionKeyProperty: this.partitionKeyProperties?.[0],
|
||||||
collectionId: this.id(),
|
collectionId: this.id(),
|
||||||
databaseId: this.databaseId,
|
databaseId: this.databaseId,
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -28,11 +23,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
],
|
],
|
||||||
"className": "collectionNode",
|
"className": "collectionNode",
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -55,11 +45,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -80,11 +65,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
],
|
],
|
||||||
"className": "collectionNode",
|
"className": "collectionNode",
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -143,11 +123,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -163,11 +138,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
],
|
],
|
||||||
"className": "collectionNode",
|
"className": "collectionNode",
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -217,11 +187,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -292,11 +257,6 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Ca
|
|||||||
],
|
],
|
||||||
"className": "collectionNode",
|
"className": "collectionNode",
|
||||||
"contextMenu": [
|
"contextMenu": [
|
||||||
{
|
|
||||||
"iconSrc": {},
|
|
||||||
"label": "Open Cassandra Shell",
|
|
||||||
"onClick": [Function],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Delete Table",
|
"label": "Delete Table",
|
||||||
@@ -363,7 +323,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -394,7 +354,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -426,7 +386,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -462,7 +422,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -530,7 +490,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -561,7 +521,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -620,7 +580,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -706,7 +666,7 @@ exports[`createDatabaseTreeNodes generates the correct tree structure for the Mo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconSrc": {},
|
"iconSrc": {},
|
||||||
"label": "Open Mongo Shell",
|
"label": "New Shell",
|
||||||
"onClick": [Function],
|
"onClick": [Function],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
|||||||
import { useSelectedNode } from "../useSelectedNode";
|
import { useSelectedNode } from "../useSelectedNode";
|
||||||
|
|
||||||
export const shouldShowScriptNodes = (): boolean => {
|
export const shouldShowScriptNodes = (): boolean => {
|
||||||
return !isFabric() && configContext.platform !== Platform.Emulator && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
return !isFabric() && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
|
||||||
};
|
};
|
||||||
|
|
||||||
const TreeDatabaseIcon = <DatabaseRegular fontSize={16} />;
|
const TreeDatabaseIcon = <DatabaseRegular fontSize={16} />;
|
||||||
|
|||||||
@@ -34,8 +34,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const { isLoggedIn, armToken, graphToken, account, tenantId, logout, login, switchTenant, authFailure } =
|
const { isLoggedIn, armToken, graphToken, account, tenantId, logout, login, switchTenant, authFailure } =
|
||||||
useAADAuth(config);
|
useAADAuth();
|
||||||
|
|
||||||
const [databaseAccount, setDatabaseAccount] = React.useState<DatabaseAccount>();
|
const [databaseAccount, setDatabaseAccount] = React.useState<DatabaseAccount>();
|
||||||
const [authType, setAuthType] = React.useState<AuthType>(encryptedToken ? AuthType.EncryptedToken : undefined);
|
const [authType, setAuthType] = React.useState<AuthType>(encryptedToken ? AuthType.EncryptedToken : undefined);
|
||||||
const [connectionString, setConnectionString] = React.useState<string>();
|
const [connectionString, setConnectionString] = React.useState<string>();
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ export class PhoenixClient {
|
|||||||
|
|
||||||
private getPhoenixControlPlanePathPrefix(): string {
|
private getPhoenixControlPlanePathPrefix(): string {
|
||||||
if (!this.armResourceId) {
|
if (!this.armResourceId) {
|
||||||
throw new Error("The Phoenix client was not initialized properly: missing ARM resource id");
|
throw new Error("The Phoenix client was not initialized properly: missing ARM resourcce id");
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolsEndpoint =
|
const toolsEndpoint =
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
||||||
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
||||||
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
||||||
enableCloudShell: true,
|
enableCloudShell: "true" === get("enablecloudshell"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ export const getOfferingIds = async (regions: Array<RegionItem>): Promise<Offeri
|
|||||||
host: configContext.CATALOG_ENDPOINT,
|
host: configContext.CATALOG_ENDPOINT,
|
||||||
path: getOfferingIdPathForRegion(),
|
path: getOfferingIdPathForRegion(),
|
||||||
method: "GET",
|
method: "GET",
|
||||||
apiVersion: configContext.CATALOG_API_VERSION,
|
apiVersion: "2023-05-01-preview",
|
||||||
queryParams: {
|
queryParams: {
|
||||||
filter:
|
filter:
|
||||||
"armRegionName eq '" +
|
"armRegionName eq '" +
|
||||||
|
|||||||
@@ -91,5 +91,5 @@ export const getItemName = (): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isDataplaneRbacSupported = (apiType: string): boolean => {
|
export const isDataplaneRbacSupported = (apiType: string): boolean => {
|
||||||
return apiType === "SQL" || apiType === "Tables" || apiType === "Gremlin";
|
return apiType === "SQL" || apiType === "Tables";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,51 +1,10 @@
|
|||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
import { ApiType, updateUserContext, userContext } from "../UserContext";
|
import { updateUserContext } from "../UserContext";
|
||||||
import * as AuthorizationUtils from "./AuthorizationUtils";
|
import * as AuthorizationUtils from "./AuthorizationUtils";
|
||||||
jest.mock("../Explorer/Explorer");
|
jest.mock("../Explorer/Explorer");
|
||||||
|
|
||||||
describe("AuthorizationUtils", () => {
|
describe("AuthorizationUtils", () => {
|
||||||
const setAadDataPlane = (enabled: boolean) => {
|
|
||||||
updateUserContext({
|
|
||||||
features: {
|
|
||||||
enableAadDataPlane: enabled,
|
|
||||||
canExceedMaximumValue: false,
|
|
||||||
cosmosdb: false,
|
|
||||||
enableChangeFeedPolicy: false,
|
|
||||||
enableFixedCollectionWithSharedThroughput: false,
|
|
||||||
enableKOPanel: false,
|
|
||||||
enableNotebooks: false,
|
|
||||||
enableReactPane: false,
|
|
||||||
enableRightPanelV2: false,
|
|
||||||
enableSchema: false,
|
|
||||||
enableSDKoperations: false,
|
|
||||||
enableSpark: false,
|
|
||||||
enableTtl: false,
|
|
||||||
executeSproc: false,
|
|
||||||
enableResourceGraph: false,
|
|
||||||
enableKoResourceTree: false,
|
|
||||||
enableThroughputBuckets: false,
|
|
||||||
hostedDataExplorer: false,
|
|
||||||
sandboxNotebookOutputs: false,
|
|
||||||
showMinRUSurvey: false,
|
|
||||||
ttl90Days: false,
|
|
||||||
enableThroughputCap: false,
|
|
||||||
enableHierarchicalKeys: false,
|
|
||||||
enableCopilot: false,
|
|
||||||
disableCopilotPhoenixGateaway: false,
|
|
||||||
enableCopilotFullSchema: false,
|
|
||||||
copilotChatFixedMonacoEditorHeight: false,
|
|
||||||
enablePriorityBasedExecution: false,
|
|
||||||
disableConnectionStringLogin: false,
|
|
||||||
enableCloudShell: false,
|
|
||||||
autoscaleDefault: false,
|
|
||||||
partitionKeyDefault: false,
|
|
||||||
partitionKeyDefault2: false,
|
|
||||||
notebooksDownBanner: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("getAuthorizationHeader()", () => {
|
describe("getAuthorizationHeader()", () => {
|
||||||
it("should return authorization header if authentication type is AAD", () => {
|
it("should return authorization header if authentication type is AAD", () => {
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
@@ -95,41 +54,4 @@ describe("AuthorizationUtils", () => {
|
|||||||
).toBeDefined();
|
).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("useDataplaneRbacAuthorization()", () => {
|
|
||||||
it("should return true if enableAadDataPlane feature flag is set", () => {
|
|
||||||
setAadDataPlane(true);
|
|
||||||
expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return true if dataPlaneRbacEnabled is set to true and API supports RBAC", () => {
|
|
||||||
setAadDataPlane(false);
|
|
||||||
["SQL", "Tables", "Gremlin"].forEach((type) => {
|
|
||||||
updateUserContext({
|
|
||||||
dataPlaneRbacEnabled: true,
|
|
||||||
apiType: type as ApiType,
|
|
||||||
});
|
|
||||||
expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false if dataPlaneRbacEnabled is set to true and API does not support RBAC", () => {
|
|
||||||
setAadDataPlane(false);
|
|
||||||
["Mongo", "Cassandra", "Postgres", "VCoreMongo"].forEach((type) => {
|
|
||||||
updateUserContext({
|
|
||||||
dataPlaneRbacEnabled: true,
|
|
||||||
apiType: type as ApiType,
|
|
||||||
});
|
|
||||||
expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false if dataPlaneRbacEnabled is set to false", () => {
|
|
||||||
setAadDataPlane(false);
|
|
||||||
updateUserContext({
|
|
||||||
dataPlaneRbacEnabled: false,
|
|
||||||
});
|
|
||||||
expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as msal from "@azure/msal-browser";
|
import * as msal from "@azure/msal-browser";
|
||||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import { isDataplaneRbacSupported } from "Utils/APITypeUtils";
|
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
import * as Logger from "../Common/Logger";
|
import * as Logger from "../Common/Logger";
|
||||||
@@ -8,7 +7,7 @@ import { configContext } from "../ConfigContext";
|
|||||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { trace, traceFailure } from "../Shared/Telemetry/TelemetryProcessor";
|
import { trace, traceFailure } from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { UserContext, userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
|
|
||||||
export function getAuthorizationHeader(): ViewModels.AuthorizationTokenHeaderMetadata {
|
export function getAuthorizationHeader(): ViewModels.AuthorizationTokenHeaderMetadata {
|
||||||
if (userContext.authType === AuthType.EncryptedToken) {
|
if (userContext.authType === AuthType.EncryptedToken) {
|
||||||
@@ -180,10 +179,3 @@ export async function acquireTokenWithMsal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDataplaneRbacAuthorization(userContext: UserContext): boolean {
|
|
||||||
return (
|
|
||||||
userContext.features.enableAadDataPlane ||
|
|
||||||
(userContext.dataPlaneRbacEnabled && isDataplaneRbacSupported(userContext.apiType))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export const autoPilotThroughput1K = 1000;
|
export const autoPilotThroughput1K = 1000;
|
||||||
export const autoPilotIncrementStep = 1000;
|
export const autoPilotIncrementStep = 1000;
|
||||||
export const autoPilotThroughput4K = 4000;
|
export const autoPilotThroughput4K = 4000;
|
||||||
export const autoPilotThroughput5K = 5000;
|
|
||||||
export const autoPilotThroughput10K = 10000;
|
export const autoPilotThroughput10K = 10000;
|
||||||
|
|
||||||
export function isValidAutoPilotThroughput(maxThroughput: number): boolean {
|
export function isValidAutoPilotThroughput(maxThroughput: number): boolean {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
|
||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
|
|
||||||
@@ -19,8 +18,5 @@ export const isServerlessAccount = (): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isVectorSearchEnabled = (): boolean => {
|
export const isVectorSearchEnabled = (): boolean => {
|
||||||
return (
|
return userContext.apiType === "SQL" && isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLVectorSearch);
|
||||||
userContext.apiType === "SQL" &&
|
|
||||||
(isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLVectorSearch) || isFabricNative())
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,25 +45,32 @@ export const defaultAllowedArmEndpoints: ReadonlyArray<string> = [
|
|||||||
"https://management.chinacloudapi.cn",
|
"https://management.chinacloudapi.cn",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const defaultAllowedAadEndpoints: ReadonlyArray<string> = [
|
export const allowedAadEndpoints: ReadonlyArray<string> = [
|
||||||
"https://login.microsoftonline.com/",
|
"https://login.microsoftonline.com/",
|
||||||
"https://login.microsoftonline.us/",
|
"https://login.microsoftonline.us/",
|
||||||
"https://login.partner.microsoftonline.cn/",
|
"https://login.partner.microsoftonline.cn/",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const defaultAllowedGraphEndpoints: ReadonlyArray<string> = ["https://graph.microsoft.com"];
|
|
||||||
|
|
||||||
export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
|
export const defaultAllowedBackendEndpoints: ReadonlyArray<string> = [
|
||||||
|
"https://localhost:12901",
|
||||||
"https://localhost:1234",
|
"https://localhost:1234",
|
||||||
PortalBackendEndpoints.Development,
|
|
||||||
PortalBackendEndpoints.Mpac,
|
|
||||||
PortalBackendEndpoints.Prod,
|
|
||||||
PortalBackendEndpoints.Fairfax,
|
|
||||||
PortalBackendEndpoints.Mooncake,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const PortalBackendOutboundIPs: { [key: string]: string[] } = {
|
||||||
|
[PortalBackendEndpoints.Mpac]: ["13.91.105.215", "4.210.172.107"],
|
||||||
|
[PortalBackendEndpoints.Prod]: ["13.88.56.148", "40.91.218.243"],
|
||||||
|
[PortalBackendEndpoints.Fairfax]: ["52.247.163.6", "52.244.134.181"],
|
||||||
|
[PortalBackendEndpoints.Mooncake]: ["163.228.137.6", "143.64.170.142"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MongoProxyOutboundIPs: { [key: string]: string[] } = {
|
||||||
|
[MongoProxyEndpoints.Mpac]: ["20.245.81.54", "40.118.23.126"],
|
||||||
|
[MongoProxyEndpoints.Prod]: ["40.80.152.199", "13.95.130.121"],
|
||||||
|
[MongoProxyEndpoints.Fairfax]: ["52.244.176.112", "52.247.148.42"],
|
||||||
|
[MongoProxyEndpoints.Mooncake]: ["52.131.240.99", "143.64.61.130"],
|
||||||
|
};
|
||||||
|
|
||||||
export const defaultAllowedMongoProxyEndpoints: ReadonlyArray<string> = [
|
export const defaultAllowedMongoProxyEndpoints: ReadonlyArray<string> = [
|
||||||
"https://localhost:1234",
|
|
||||||
MongoProxyEndpoints.Development,
|
MongoProxyEndpoints.Development,
|
||||||
MongoProxyEndpoints.Mpac,
|
MongoProxyEndpoints.Mpac,
|
||||||
MongoProxyEndpoints.Prod,
|
MongoProxyEndpoints.Prod,
|
||||||
@@ -79,14 +86,6 @@ export const defaultAllowedCassandraProxyEndpoints: ReadonlyArray<string> = [
|
|||||||
CassandraProxyEndpoints.Mooncake,
|
CassandraProxyEndpoints.Mooncake,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const allowedCassandraProxyEndpoints_ToBeDeprecated: ReadonlyArray<string> = [
|
|
||||||
"https://main.documentdb.ext.azure.com",
|
|
||||||
"https://main.documentdb.ext.azure.cn",
|
|
||||||
"https://main.documentdb.ext.azure.us",
|
|
||||||
"https://main.cosmos.ext.azure",
|
|
||||||
"https://localhost:12901",
|
|
||||||
];
|
|
||||||
|
|
||||||
export const CassandraProxyOutboundIPs: { [key: string]: string[] } = {
|
export const CassandraProxyOutboundIPs: { [key: string]: string[] } = {
|
||||||
[CassandraProxyEndpoints.Mpac]: ["40.113.96.14", "104.42.11.145"],
|
[CassandraProxyEndpoints.Mpac]: ["40.113.96.14", "104.42.11.145"],
|
||||||
[CassandraProxyEndpoints.Prod]: ["137.117.230.240", "168.61.72.237"],
|
[CassandraProxyEndpoints.Prod]: ["137.117.230.240", "168.61.72.237"],
|
||||||
@@ -94,7 +93,11 @@ export const CassandraProxyOutboundIPs: { [key: string]: string[] } = {
|
|||||||
[CassandraProxyEndpoints.Mooncake]: ["40.73.99.146", "143.64.62.47"],
|
[CassandraProxyEndpoints.Mooncake]: ["40.73.99.146", "143.64.62.47"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const allowedEmulatorEndpoints: ReadonlyArray<string> = ["https://localhost:8081", "http://localhost:8081"];
|
export const allowedEmulatorEndpoints: ReadonlyArray<string> = ["https://localhost:8081"];
|
||||||
|
|
||||||
|
export const allowedMongoBackendEndpoints: ReadonlyArray<string> = ["https://localhost:1234"];
|
||||||
|
|
||||||
|
export const allowedGraphEndpoints: ReadonlyArray<string> = ["https://graph.microsoft.com"];
|
||||||
|
|
||||||
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];
|
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];
|
||||||
|
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
import { Platform, configContext } from "../ConfigContext";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feature flags enumeration - centralized feature definitions
|
|
||||||
*/
|
|
||||||
export enum PlatformFeature {
|
|
||||||
// UI/Core Features
|
|
||||||
Queries = "Queries",
|
|
||||||
Notebooks = "Notebooks",
|
|
||||||
SynapseLink = "SynapseLink",
|
|
||||||
VSCodeIntegration = "VSCodeIntegration",
|
|
||||||
GlobalSecondaryIndex = "GlobalSecondaryIndex",
|
|
||||||
DataPlaneRbac = "DataPlaneRbac",
|
|
||||||
EntraIDLogin = "EntraIDLogin",
|
|
||||||
EntreIDRbac = "EntreIDRbac",
|
|
||||||
RetrySettings = "RetrySettings",
|
|
||||||
GraphAutoVizOption = "GraphAutoVizOption",
|
|
||||||
CrossPartitionOption = "CrossPartitionOption",
|
|
||||||
EnhancedQueryControl = "EnhancedQueryControl",
|
|
||||||
ParallelismOption = "ParallelismOption",
|
|
||||||
EnableEntraIdRbac = "EnableEntraIdRbac",
|
|
||||||
PriorityBasedExecution = "PriorityBasedExecution",
|
|
||||||
RegionSelection = "RegionSelection",
|
|
||||||
Copilot = "Copilot",
|
|
||||||
CloudShell = "CloudShell",
|
|
||||||
ContainerPagination = "ContainerPagination",
|
|
||||||
FullTextSearch = "FullTextSearch",
|
|
||||||
VectorSearch = "VectorSearch",
|
|
||||||
ThroughputBucketing = "ThroughputBucketing",
|
|
||||||
ComputedProperties = "ComputedProperties",
|
|
||||||
AnalyticalStore = "AnalyticalStore",
|
|
||||||
|
|
||||||
// CRUD Operations - Database
|
|
||||||
CreateDatabase = "CreateDatabase",
|
|
||||||
ReadDatabase = "ReadDatabase",
|
|
||||||
DeleteDatabase = "DeleteDatabase",
|
|
||||||
|
|
||||||
// CRUD Operations - Collection
|
|
||||||
CreateCollection = "CreateCollection",
|
|
||||||
ReadCollection = "ReadCollection",
|
|
||||||
UpdateCollection = "UpdateCollection",
|
|
||||||
DeleteCollection = "DeleteCollection",
|
|
||||||
|
|
||||||
// CRUD Operations - Document
|
|
||||||
CreateDocument = "CreateDocument",
|
|
||||||
ReadDocument = "ReadDocument",
|
|
||||||
UpdateDocument = "UpdateDocument",
|
|
||||||
DeleteDocument = "DeleteDocument",
|
|
||||||
|
|
||||||
// Advanced Database Features
|
|
||||||
StoredProcedures = "StoredProcedures",
|
|
||||||
UDF = "UDF",
|
|
||||||
Trigger = "Trigger",
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Feature matrix per platform.
|
|
||||||
* - Only list platforms that have restrictions. If a platform is not present, all features are considered supported.
|
|
||||||
* - Start with VNextEmulator today; add more platforms/flags here later without touching calling code.
|
|
||||||
*/
|
|
||||||
const FEATURE_MATRIX: ReadonlyMap<Platform, ReadonlySet<PlatformFeature>> = new Map([
|
|
||||||
[
|
|
||||||
Platform.VNextEmulator,
|
|
||||||
new Set<PlatformFeature>([
|
|
||||||
PlatformFeature.Queries,
|
|
||||||
|
|
||||||
PlatformFeature.CreateDatabase,
|
|
||||||
PlatformFeature.ReadDatabase,
|
|
||||||
PlatformFeature.DeleteDatabase,
|
|
||||||
|
|
||||||
PlatformFeature.CreateCollection,
|
|
||||||
PlatformFeature.ReadCollection,
|
|
||||||
PlatformFeature.UpdateCollection,
|
|
||||||
PlatformFeature.DeleteCollection,
|
|
||||||
|
|
||||||
PlatformFeature.CreateDocument,
|
|
||||||
PlatformFeature.ReadDocument,
|
|
||||||
PlatformFeature.UpdateDocument,
|
|
||||||
PlatformFeature.DeleteDocument,
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Central feature flag function - checks if a feature is enabled for current platform
|
|
||||||
* @param feature The feature to check
|
|
||||||
* @param platform Optional platform override, defaults to current platform
|
|
||||||
* @returns True if the feature is enabled for the platform, false otherwise
|
|
||||||
*/
|
|
||||||
export const isFeatureSupported = (feature: PlatformFeature, platform?: Platform): boolean => {
|
|
||||||
const currentPlatform = platform ?? configContext.platform;
|
|
||||||
if (currentPlatform !== Platform.VNextEmulator) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// VNextEmulator: check from the feature matrix
|
|
||||||
const vnextFeatures = FEATURE_MATRIX.get(Platform.VNextEmulator);
|
|
||||||
return vnextFeatures?.has(feature) ?? false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const areAdvancedScriptsSupported = (platform?: Platform): boolean => {
|
|
||||||
const currentPlatform = platform ?? configContext.platform;
|
|
||||||
if (currentPlatform !== Platform.VNextEmulator) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, require all script features to be enabled
|
|
||||||
return (
|
|
||||||
isFeatureSupported(PlatformFeature.StoredProcedures, currentPlatform) &&
|
|
||||||
isFeatureSupported(PlatformFeature.UDF, currentPlatform) &&
|
|
||||||
isFeatureSupported(PlatformFeature.Trigger, currentPlatform)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
|
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
|
||||||
export async function listCassandraKeyspaces(
|
export async function listCassandraKeyspaces(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given database account and collection. */
|
/* Retrieves the metrics determined by the given filter for the given database account and collection. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
|
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
|
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
|
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given database account and database. */
|
/* Retrieves the metrics determined by the given filter for the given database account and database. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given database account and region. */
|
/* Retrieves the metrics determined by the given filter for the given database account and region. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the properties of an existing Azure Cosmos DB database account. */
|
/* Retrieves the properties of an existing Azure Cosmos DB database account. */
|
||||||
export async function get(
|
export async function get(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists the graphs under an existing Azure Cosmos DB database account. */
|
/* Lists the graphs under an existing Azure Cosmos DB database account. */
|
||||||
export async function listGraphs(
|
export async function listGraphs(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
|
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
|
||||||
export async function listGremlinDatabases(
|
export async function listGremlinDatabases(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* List Cosmos DB locations and their properties */
|
/* List Cosmos DB locations and their properties */
|
||||||
export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> {
|
export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> {
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
|
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
|
||||||
export async function listMongoDBDatabases(
|
export async function listMongoDBDatabases(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists all of the available Cosmos DB Resource Provider operations. */
|
/* Lists all of the available Cosmos DB Resource Provider operations. */
|
||||||
export async function list(): Promise<Types.OperationListResult> {
|
export async function list(): Promise<Types.OperationListResult> {
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given partition key range id. */
|
/* Retrieves the metrics determined by the given filter for the given partition key range id. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given partition key range id and region. */
|
/* Retrieves the metrics determined by the given filter for the given partition key range id and region. */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given database account. This url is only for PBS and Replication Latency data */
|
/* Retrieves the metrics determined by the given filter for the given database account. This url is only for PBS and Replication Latency data */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given account, source and target region. This url is only for PBS and Replication Latency data */
|
/* Retrieves the metrics determined by the given filter for the given account, source and target region. This url is only for PBS and Replication Latency data */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Retrieves the metrics determined by the given filter for the given account target region. This url is only for PBS and Replication Latency data */
|
/* Retrieves the metrics determined by the given filter for the given account target region. This url is only for PBS and Replication Latency data */
|
||||||
export async function listMetrics(
|
export async function listMetrics(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists the SQL databases under an existing Azure Cosmos DB database account. */
|
/* Lists the SQL databases under an existing Azure Cosmos DB database account. */
|
||||||
export async function listSqlDatabases(
|
export async function listSqlDatabases(
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { configContext } from "../../../../ConfigContext";
|
||||||
import { armRequest } from "../../request";
|
import { armRequest } from "../../request";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import { configContext } from "../../../../ConfigContext";
|
const apiVersion = "2024-12-01-preview";
|
||||||
const apiVersion = "2025-05-01-preview";
|
|
||||||
|
|
||||||
/* Lists the Tables under an existing Azure Cosmos DB database account. */
|
/* Lists the Tables under an existing Azure Cosmos DB database account. */
|
||||||
export async function listTables(
|
export async function listTables(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Run "npm run generateARMClients" to regenerate
|
Run "npm run generateARMClients" to regenerate
|
||||||
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
|
||||||
|
|
||||||
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
|
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2024-12-01-preview/cosmos-db.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The List operation response, that contains the client encryption keys and their properties. */
|
/* The List operation response, that contains the client encryption keys and their properties. */
|
||||||
@@ -580,8 +580,6 @@ export interface DatabaseAccountGetProperties {
|
|||||||
|
|
||||||
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
||||||
enablePerRegionPerPartitionAutoscale?: boolean;
|
enablePerRegionPerPartitionAutoscale?: boolean;
|
||||||
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
|
|
||||||
enableAllVersionsAndDeletesChangeFeed?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Properties to create and update Azure Cosmos DB database accounts. */
|
/* Properties to create and update Azure Cosmos DB database accounts. */
|
||||||
@@ -684,8 +682,6 @@ export interface DatabaseAccountCreateUpdateProperties {
|
|||||||
|
|
||||||
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
||||||
enablePerRegionPerPartitionAutoscale?: boolean;
|
enablePerRegionPerPartitionAutoscale?: boolean;
|
||||||
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
|
|
||||||
enableAllVersionsAndDeletesChangeFeed?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parameters to create and update Cosmos DB database accounts. */
|
/* Parameters to create and update Cosmos DB database accounts. */
|
||||||
@@ -791,8 +787,6 @@ export interface DatabaseAccountUpdateProperties {
|
|||||||
|
|
||||||
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
/* Flag to indicate enabling/disabling of Per-Region Per-partition autoscale Preview feature on the account */
|
||||||
enablePerRegionPerPartitionAutoscale?: boolean;
|
enablePerRegionPerPartitionAutoscale?: boolean;
|
||||||
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
|
|
||||||
enableAllVersionsAndDeletesChangeFeed?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parameters for patching Azure Cosmos DB database account properties. */
|
/* Parameters for patching Azure Cosmos DB database account properties. */
|
||||||
@@ -1222,8 +1216,6 @@ export interface PhysicalPartitionThroughputInfoResource {
|
|||||||
id: string;
|
id: string;
|
||||||
/* Throughput of a physical partition */
|
/* Throughput of a physical partition */
|
||||||
throughput?: number;
|
throughput?: number;
|
||||||
/* Target throughput of a physical partition */
|
|
||||||
targetThroughput?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cosmos DB client encryption key resource object. */
|
/* Cosmos DB client encryption key resource object. */
|
||||||
@@ -1293,16 +1285,12 @@ export interface SqlContainerResource {
|
|||||||
/* The configuration for defining Materialized Views. This must be specified only for creating a Materialized View container. */
|
/* The configuration for defining Materialized Views. This must be specified only for creating a Materialized View container. */
|
||||||
materializedViewDefinition?: MaterializedViewDefinition;
|
materializedViewDefinition?: MaterializedViewDefinition;
|
||||||
|
|
||||||
/* Materialized Views defined on the container. */
|
|
||||||
materializedViews?: MaterializedViewDetails[];
|
|
||||||
|
|
||||||
/* List of computed properties */
|
/* List of computed properties */
|
||||||
computedProperties?: ComputedProperty[];
|
computedProperties?: ComputedProperty[];
|
||||||
|
|
||||||
/* The vector embedding policy for the container. */
|
/* The vector embedding policy for the container. */
|
||||||
vectorEmbeddingPolicy?: VectorEmbeddingPolicy;
|
vectorEmbeddingPolicy?: VectorEmbeddingPolicy;
|
||||||
|
|
||||||
/* The FullText policy for the container. */
|
|
||||||
fullTextPolicy?: FullTextPolicy;
|
fullTextPolicy?: FullTextPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1335,14 +1323,6 @@ export interface VectorEmbeddingPolicy {
|
|||||||
vectorEmbeddings?: VectorEmbedding[];
|
vectorEmbeddings?: VectorEmbedding[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cosmos DB FullText Policy */
|
|
||||||
export interface FullTextPolicy {
|
|
||||||
/* The default language for a full text paths. */
|
|
||||||
defaultLanguage?: string;
|
|
||||||
/* List of FullText Paths */
|
|
||||||
fullTextPaths?: FullTextPath[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* undocumented */
|
/* undocumented */
|
||||||
export interface ExcludedPath {
|
export interface ExcludedPath {
|
||||||
/* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */
|
/* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */
|
||||||
@@ -1381,7 +1361,7 @@ export interface VectorEmbedding {
|
|||||||
/* The path to the vector field in the document. */
|
/* The path to the vector field in the document. */
|
||||||
path: string;
|
path: string;
|
||||||
/* Indicates the data type of vector. */
|
/* Indicates the data type of vector. */
|
||||||
dataType: "float32" | "uint8" | "int8";
|
dataType: "float16" | "float32" | "uint8" | "int8";
|
||||||
|
|
||||||
/* The distance function to use for distance calculation in between vectors. */
|
/* The distance function to use for distance calculation in between vectors. */
|
||||||
distanceFunction: "euclidean" | "cosine" | "dotproduct";
|
distanceFunction: "euclidean" | "cosine" | "dotproduct";
|
||||||
@@ -1390,12 +1370,26 @@ export interface VectorEmbedding {
|
|||||||
dimensions: number;
|
dimensions: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Represents the full text path specification. */
|
export interface FullTextPolicy {
|
||||||
|
/**
|
||||||
|
* The default language for the full text .
|
||||||
|
*/
|
||||||
|
defaultLanguage: string;
|
||||||
|
/**
|
||||||
|
* The paths to be indexed for full text search.
|
||||||
|
*/
|
||||||
|
fullTextPaths: FullTextPath[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface FullTextPath {
|
export interface FullTextPath {
|
||||||
/* The path to the full text field in the document. */
|
/**
|
||||||
|
* The path to be indexed for full text search.
|
||||||
|
*/
|
||||||
path: string;
|
path: string;
|
||||||
/* The language of the full text field in the document. */
|
/**
|
||||||
language?: string;
|
* The language for the full text path.
|
||||||
|
*/
|
||||||
|
language: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* List of composite path */
|
/* List of composite path */
|
||||||
@@ -1499,14 +1493,6 @@ export interface MaterializedViewDefinition {
|
|||||||
definition: string;
|
definition: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MaterializedViewDetails, contains Id & _rid fields of materialized view. */
|
|
||||||
export interface MaterializedViewDetails {
|
|
||||||
/* Id field of Materialized container. */
|
|
||||||
id?: string;
|
|
||||||
/* _rid field of Materialized container. */
|
|
||||||
_rid?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cosmos DB SQL storedProcedure resource object */
|
/* Cosmos DB SQL storedProcedure resource object */
|
||||||
export interface SqlStoredProcedureResource {
|
export interface SqlStoredProcedureResource {
|
||||||
/* Name of the Cosmos DB SQL storedProcedure */
|
/* Name of the Cosmos DB SQL storedProcedure */
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import * as msal from "@azure/msal-browser";
|
import * as msal from "@azure/msal-browser";
|
||||||
import { useBoolean } from "@fluentui/react-hooks";
|
import { useBoolean } from "@fluentui/react-hooks";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ConfigContext } from "../ConfigContext";
|
import { configContext } from "../ConfigContext";
|
||||||
import { acquireTokenWithMsal, getMsalInstance } from "../Utils/AuthorizationUtils";
|
import { acquireTokenWithMsal, getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||||
|
|
||||||
|
const msalInstance = await getMsalInstance();
|
||||||
|
|
||||||
|
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||||
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
||||||
|
|
||||||
interface ReturnType {
|
interface ReturnType {
|
||||||
@@ -24,97 +27,57 @@ export interface AadAuthFailure {
|
|||||||
failureLinkAction?: () => void;
|
failureLinkAction?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAADAuth(config?: ConfigContext): ReturnType {
|
export function useAADAuth(): ReturnType {
|
||||||
const [msalInstance, setMsalInstance] = React.useState<msal.PublicClientApplication | null>(null);
|
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(
|
||||||
const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(false);
|
Boolean(cachedAccount && cachedTenantId) || false,
|
||||||
const [account, setAccount] = React.useState<msal.AccountInfo>(null);
|
);
|
||||||
|
const [account, setAccount] = React.useState<msal.AccountInfo>(cachedAccount);
|
||||||
const [tenantId, setTenantId] = React.useState<string>(cachedTenantId);
|
const [tenantId, setTenantId] = React.useState<string>(cachedTenantId);
|
||||||
const [graphToken, setGraphToken] = React.useState<string>();
|
const [graphToken, setGraphToken] = React.useState<string>();
|
||||||
const [armToken, setArmToken] = React.useState<string>();
|
const [armToken, setArmToken] = React.useState<string>();
|
||||||
const [authFailure, setAuthFailure] = React.useState<AadAuthFailure>(undefined);
|
const [authFailure, setAuthFailure] = React.useState<AadAuthFailure>(undefined);
|
||||||
|
|
||||||
// Initialize MSAL instance when config is available
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (config && !msalInstance) {
|
|
||||||
getMsalInstance().then((instance) => {
|
|
||||||
setMsalInstance(instance);
|
|
||||||
const cachedAccount = instance.getAllAccounts()?.[0];
|
|
||||||
if (cachedAccount && cachedTenantId) {
|
|
||||||
setAccount(cachedAccount);
|
|
||||||
setLoggedIn();
|
|
||||||
instance.setActiveAccount(cachedAccount);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [config, msalInstance]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (msalInstance && account) {
|
|
||||||
msalInstance.setActiveAccount(account);
|
msalInstance.setActiveAccount(account);
|
||||||
}
|
|
||||||
}, [msalInstance, account]);
|
|
||||||
|
|
||||||
const login = React.useCallback(async () => {
|
const login = React.useCallback(async () => {
|
||||||
if (!msalInstance || !config) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await msalInstance.loginPopup({
|
const response = await msalInstance.loginPopup({
|
||||||
redirectUri: config.msalRedirectURI,
|
redirectUri: configContext.msalRedirectURI,
|
||||||
scopes: [],
|
scopes: [],
|
||||||
});
|
});
|
||||||
setLoggedIn();
|
setLoggedIn();
|
||||||
setAccount(response.account);
|
setAccount(response.account);
|
||||||
setTenantId(response.tenantId);
|
setTenantId(response.tenantId);
|
||||||
localStorage.setItem("cachedTenantId", response.tenantId);
|
localStorage.setItem("cachedTenantId", response.tenantId);
|
||||||
} catch (error) {
|
}, []);
|
||||||
setAuthFailure({
|
|
||||||
failureMessage: `Login failed: ${JSON.stringify(error)}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [msalInstance, config]);
|
|
||||||
|
|
||||||
const logout = React.useCallback(() => {
|
const logout = React.useCallback(() => {
|
||||||
if (!msalInstance) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLoggedOut();
|
setLoggedOut();
|
||||||
localStorage.removeItem("cachedTenantId");
|
localStorage.removeItem("cachedTenantId");
|
||||||
msalInstance.logoutRedirect();
|
msalInstance.logoutRedirect();
|
||||||
}, [msalInstance]);
|
}, []);
|
||||||
|
|
||||||
const switchTenant = React.useCallback(
|
const switchTenant = React.useCallback(
|
||||||
async (id) => {
|
async (id) => {
|
||||||
if (!msalInstance || !config) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await msalInstance.loginPopup({
|
const response = await msalInstance.loginPopup({
|
||||||
redirectUri: config.msalRedirectURI,
|
redirectUri: configContext.msalRedirectURI,
|
||||||
authority: `${config.AAD_ENDPOINT}${id}`,
|
authority: `${configContext.AAD_ENDPOINT}${id}`,
|
||||||
scopes: [],
|
scopes: [],
|
||||||
});
|
});
|
||||||
setTenantId(response.tenantId);
|
setTenantId(response.tenantId);
|
||||||
setAccount(response.account);
|
setAccount(response.account);
|
||||||
localStorage.setItem("cachedTenantId", response.tenantId);
|
localStorage.setItem("cachedTenantId", response.tenantId);
|
||||||
} catch (error) {
|
|
||||||
setAuthFailure({
|
|
||||||
failureMessage: `Tenant switch failed: ${JSON.stringify(error)}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[msalInstance, config],
|
[account, tenantId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const acquireTokens = React.useCallback(async () => {
|
const acquireTokens = React.useCallback(async () => {
|
||||||
if (!(account && tenantId && msalInstance && config)) {
|
if (!(account && tenantId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const armToken = await acquireTokenWithMsal(msalInstance, {
|
const armToken = await acquireTokenWithMsal(msalInstance, {
|
||||||
authority: `${config.AAD_ENDPOINT}${tenantId}`,
|
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||||
scopes: [`${config.ARM_ENDPOINT}/.default`],
|
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||||
});
|
});
|
||||||
|
|
||||||
setArmToken(armToken);
|
setArmToken(armToken);
|
||||||
@@ -142,8 +105,8 @@ export function useAADAuth(config?: ConfigContext): ReturnType {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const graphToken = await acquireTokenWithMsal(msalInstance, {
|
const graphToken = await acquireTokenWithMsal(msalInstance, {
|
||||||
authority: `${config.AAD_ENDPOINT}${tenantId}`,
|
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||||
scopes: [`${config.GRAPH_ENDPOINT}/.default`],
|
scopes: [`${configContext.GRAPH_ENDPOINT}/.default`],
|
||||||
});
|
});
|
||||||
|
|
||||||
setGraphToken(graphToken);
|
setGraphToken(graphToken);
|
||||||
@@ -152,7 +115,7 @@ export function useAADAuth(config?: ConfigContext): ReturnType {
|
|||||||
// it's not critical if this fails.
|
// it's not critical if this fails.
|
||||||
console.warn("Error acquiring graph token: " + error);
|
console.warn("Error acquiring graph token: " + error);
|
||||||
}
|
}
|
||||||
}, [account, tenantId, msalInstance, config]);
|
}, [account, tenantId]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (account && tenantId && !authFailure) {
|
if (account && tenantId && !authFailure) {
|
||||||
|
|||||||
@@ -64,8 +64,7 @@ import {
|
|||||||
getMsalInstance,
|
getMsalInstance,
|
||||||
} from "../Utils/AuthorizationUtils";
|
} from "../Utils/AuthorizationUtils";
|
||||||
import { isInvalidParentFrameOrigin, shouldProcessMessage } from "../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin, shouldProcessMessage } from "../Utils/MessageValidation";
|
||||||
import { get, getReadOnlyKeys, listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
import { getReadOnlyKeys, listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
import * as Types from "../Utils/arm/generatedClients/cosmos/types";
|
|
||||||
import { applyExplorerBindings } from "../applyExplorerBindings";
|
import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||||
|
|
||||||
// This hook will create a new instance of Explorer.ts and bind it to the DOM
|
// This hook will create a new instance of Explorer.ts and bind it to the DOM
|
||||||
@@ -86,7 +85,7 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
|
|||||||
let explorer: Explorer;
|
let explorer: Explorer;
|
||||||
if (platform === Platform.Hosted) {
|
if (platform === Platform.Hosted) {
|
||||||
explorer = await configureHosted();
|
explorer = await configureHosted();
|
||||||
} else if (platform === Platform.Emulator || platform === Platform.VNextEmulator) {
|
} else if (platform === Platform.Emulator) {
|
||||||
explorer = configureEmulator();
|
explorer = configureEmulator();
|
||||||
} else if (platform === Platform.Portal) {
|
} else if (platform === Platform.Portal) {
|
||||||
explorer = await configurePortal();
|
explorer = await configurePortal();
|
||||||
@@ -347,14 +346,6 @@ async function configureHostedWithAAD(config: AAD): Promise<Explorer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// TO DO - Remove once we have ARG API support for enableMaterializedViews property
|
|
||||||
const databaseAccount: Types.DatabaseAccountGetResults = await get(
|
|
||||||
subscriptionId,
|
|
||||||
account.resourceGroup,
|
|
||||||
account.name,
|
|
||||||
);
|
|
||||||
config.databaseAccount.properties.enableMaterializedViews = databaseAccount.properties?.enableMaterializedViews;
|
|
||||||
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
databaseAccount: config.databaseAccount,
|
databaseAccount: config.databaseAccount,
|
||||||
});
|
});
|
||||||
@@ -894,7 +885,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
|
|
||||||
const authorizationToken = inputs.authorizationToken || "";
|
const authorizationToken = inputs.authorizationToken || "";
|
||||||
const databaseAccount = inputs.databaseAccount;
|
const databaseAccount = inputs.databaseAccount;
|
||||||
const aadToken = inputs.aadToken || "";
|
|
||||||
|
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
ARM_ENDPOINT: normalizeArmEndpoint(inputs.csmEndpoint || configContext.ARM_ENDPOINT),
|
ARM_ENDPOINT: normalizeArmEndpoint(inputs.csmEndpoint || configContext.ARM_ENDPOINT),
|
||||||
@@ -907,7 +897,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
authorizationToken,
|
authorizationToken,
|
||||||
aadToken,
|
|
||||||
databaseAccount,
|
databaseAccount,
|
||||||
resourceGroup: inputs.resourceGroup,
|
resourceGroup: inputs.resourceGroup,
|
||||||
subscriptionId: inputs.subscriptionId,
|
subscriptionId: inputs.subscriptionId,
|
||||||
|
|||||||
@@ -1,291 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0" />
|
|
||||||
|
|
||||||
<title>Azure Cosmos DB Emulator</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="divQuickStart">
|
|
||||||
<div class="Introlines">
|
|
||||||
<p class="Introline1">Congratulations! Your Azure Cosmos DB emulator is running.</p>
|
|
||||||
<p class="Introline2">Now, let's connect a sample app to it.</p>
|
|
||||||
<div id="divQuickStartConnections">
|
|
||||||
<p class="Introline2">URI</p>
|
|
||||||
<input type="text" class="codeblock" readonly="readonly" value="http://localhost:8081" />
|
|
||||||
<p class="Introline2">Primary Key</p>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="codeblock"
|
|
||||||
readonly="readonly"
|
|
||||||
value="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
|
||||||
/>
|
|
||||||
<p class="Introline2">Primary Connection String</p>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="codeblock"
|
|
||||||
readonly="readonly"
|
|
||||||
value="AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p class="Introline3"><b>Choose a platform</b></p>
|
|
||||||
</div>
|
|
||||||
<div class="container-fluid">
|
|
||||||
<ul class="nav nav-tabs qslevel">
|
|
||||||
<li class="active">
|
|
||||||
<a data-toggle="tab" href="#net"
|
|
||||||
><img class="qsmenuicons" src="../images/dotnet.png" alt=".NET platform" />.NET</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a data-toggle="tab" href="#corenet"
|
|
||||||
><img class="qsmenuicons" src="../images/dotnet.png" alt=".NET Core platform" />.NET Core</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a data-toggle="tab" href="#Java"
|
|
||||||
><img class="qsmenuicons" src="../images/java.png" alt="Java platform" />Java</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a data-toggle="tab" href="#NodeJs"
|
|
||||||
><img class="qsmenuicons" src="../images/nodejs.png" alt="Node.js platform" />Node.js</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a data-toggle="tab" href="#Python"
|
|
||||||
><img class="qsmenuicons" src="../images/python.png" alt="Python platform" />Python</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="tab-content tab-content-override">
|
|
||||||
<div id="net" class="tab-pane fade in active">
|
|
||||||
<div class="netApp">
|
|
||||||
<div class="numbersize numbersizePadding">1</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Open and run a sample .NET app
|
|
||||||
<p>
|
|
||||||
We created a sample .NET app connected to your Azure Cosmos DB Emulator instance. Download, extract,
|
|
||||||
build and run the app.
|
|
||||||
</p>
|
|
||||||
<a href="quickstart/DocumentDB-Quickstart-DotNet.zip"
|
|
||||||
><button class="btncreatecoll">Download</button></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="netApp">
|
|
||||||
<div class="numbersize">2</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Learn more about Azure Cosmos DB
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/dotnet"
|
|
||||||
>Code Samples</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
|
|
||||||
>Capacity Planner</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="corenet" class="tab-pane fade">
|
|
||||||
<div class="netApp">
|
|
||||||
<div class="numbersize numbersizePadding">1</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Open and run a sample .NET Core app
|
|
||||||
<p>
|
|
||||||
We created a sample .NET Core app connected to your Azure Cosmos DB Emulator instance. Download,
|
|
||||||
extract, build and run the app.
|
|
||||||
</p>
|
|
||||||
<a href="quickstart/DocumentDB-Quickstart-DotNetCore.zip"
|
|
||||||
><button class="btncreatecoll">Download</button></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="netApp">
|
|
||||||
<div class="numbersize">2</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Learn more about Azure Cosmos DB.
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/dotnet"
|
|
||||||
>Code Samples</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
|
|
||||||
>Capacity Planner</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="Java" class="tab-pane fade">
|
|
||||||
<div class="step1">
|
|
||||||
<div class="numbersize numbersizePadding">1</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Open and run a sample Java app
|
|
||||||
<p>
|
|
||||||
We created a sample Java app connected to your Azure Cosmos DB Emulator instance. Download, extract,
|
|
||||||
build and run the app.
|
|
||||||
</p>
|
|
||||||
<a href="quickstart/DocumentDB-Quickstart-Java.zip"><button class="btncreatecoll">Download</button></a>
|
|
||||||
<p>
|
|
||||||
Follow instructions in the readme.md to setup prerequisites needed to run Java web apps, if you
|
|
||||||
haven’t already.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step1">
|
|
||||||
<div class="numbersize">2</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Learn more about Azure Cosmos DB.
|
|
||||||
<ul>
|
|
||||||
<!--<li><a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/java">Code Samples</a></li>-->
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
|
|
||||||
>Capacity Planner</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="NodeJs" class="tab-pane fade">
|
|
||||||
<div class="step1">
|
|
||||||
<div class="numbersize numbersizePadding">1</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Open and run a sample Node.js app
|
|
||||||
<p>
|
|
||||||
We created a sample Node.js app connected to your Azure Cosmos DB Emulator instance. Download,
|
|
||||||
extract, build and run the app.
|
|
||||||
</p>
|
|
||||||
<a href="quickstart/DocumentDB-Quickstart-NodeJs.zip"
|
|
||||||
><button class="btncreatecoll">Download</button></a
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Run <strong>npm install</strong> and <strong>npm start</strong>, and navigate to
|
|
||||||
<a href="http://localhost:3000" _targe="blank">http://localhost:3000</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step1">
|
|
||||||
<div class="numbersize">2</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Learn more about Azure Cosmos DB.
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/nodejs"
|
|
||||||
>Code Samples</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
|
|
||||||
>Capacity Planner</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="Python" class="tab-pane fade">
|
|
||||||
<div class="pythonApp">
|
|
||||||
<div class="numbersize numbersizePadding">1</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Create a new Python app.
|
|
||||||
<p>
|
|
||||||
Follow this
|
|
||||||
<a href="https://aka.ms/cosmos-db-emulator/tutorial/python" target="_blank">tutorial</a>
|
|
||||||
to create a new Python app connected to Azure Cosmos DB.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pythonApp">
|
|
||||||
<div class="numbersize">2</div>
|
|
||||||
<div class="numberheading">
|
|
||||||
Learn more about Azure Cosmos DB.
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/python"
|
|
||||||
>Code Samples</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
|
|
||||||
>Capacity planner</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
class="atags"
|
|
||||||
href="https://social.msdn.microsoft.com/forums/azure/home?forum=AzureDocumentDB"
|
|
||||||
>Forum</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user