mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-07 19:46:53 +00:00
Compare commits
7 Commits
users/boge
...
users/aisa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a96f4bbb46 | ||
|
|
063ad23bce | ||
|
|
eacbeae417 | ||
|
|
6493c985b4 | ||
|
|
569167fa10 | ||
|
|
6e267b2bba | ||
|
|
282004b09b |
@@ -145,5 +145,4 @@ src/Explorer/Notebook/temp/inputs/connected-editors/codemirror.tsx
|
||||
src/Explorer/Tree/ResourceTreeAdapter.tsx
|
||||
__mocks__/monaco-editor.ts
|
||||
src/Explorer/Tree/ResourceTree.tsx
|
||||
src/Utils/EndpointUtils.ts
|
||||
src/Utils/PriorityBasedExecutionUtils.ts
|
||||
@@ -53,9 +53,4 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
80
.github/workflows/ci.yml
vendored
80
.github/workflows/ci.yml
vendored
@@ -14,11 +14,11 @@ jobs:
|
||||
name: "Log Code Metrics"
|
||||
if: github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: node utils/codeMetrics.js
|
||||
env:
|
||||
@@ -27,11 +27,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Compile TypeScript"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run compile
|
||||
- run: npm run compile:strict
|
||||
@@ -39,44 +39,44 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Check Format"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run format:check
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Lint"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
unittest:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Unit Tests"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run test
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Build"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run build:contracts
|
||||
- name: Restore Build Cache
|
||||
@@ -86,10 +86,10 @@ jobs:
|
||||
key: ${{ runner.os }}-build-cache
|
||||
- run: npm run pack:prod
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||
- run: cp -r ./Contracts ./dist/contracts
|
||||
- run: cp -r ./configs ./dist/configs
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
@@ -107,11 +107,11 @@ jobs:
|
||||
if: false
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- uses: southpolesteve/cosmos-emulator-github-action@v1
|
||||
- name: End to End Tests
|
||||
run: |
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator"
|
||||
PLATFORM: "Emulator"
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: screenshots
|
||||
@@ -149,11 +149,11 @@ jobs:
|
||||
- ./test/sql/resourceToken.spec.ts
|
||||
- ./test/tables/container.spec.ts
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm start &
|
||||
- run: npm run wait-for-server
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
# Run tests up to three times
|
||||
for i in $(seq 1 3); do npx jest -c ./jest.config.playwright.js ${{ matrix['test-file'] }} && s=0 && break || s=$? && sleep 1; done; (exit $s)
|
||||
shell: bash
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: screenshots
|
||||
@@ -180,14 +180,14 @@ jobs:
|
||||
with:
|
||||
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
|
||||
- name: Download Dist Folder
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
- run: cp ./configs/prod.json config.json
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "jawelton@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
name: packages
|
||||
with:
|
||||
path: "*.nupkg"
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
with:
|
||||
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
|
||||
- name: Download Dist Folder
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
- run: cp ./configs/mpac.json config.json
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "jawelton@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
name: packages
|
||||
with:
|
||||
path: "*.nupkg"
|
||||
|
||||
4
.github/workflows/cleanup.yml
vendored
4
.github/workflows/cleanup.yml
vendored
@@ -20,9 +20,9 @@ jobs:
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 18.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: node utils/cleanupDBs.js
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,5 +16,4 @@ Contracts/*
|
||||
.env
|
||||
failure.png
|
||||
screenshots/*
|
||||
GettingStarted-ignore*.ipynb
|
||||
.vscode/
|
||||
GettingStarted-ignore*.ipynb
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -20,8 +20,8 @@
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": true
|
||||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
|
||||
"isTerminalEnabled": true,
|
||||
"isPhoenixEnabled": true
|
||||
}
|
||||
"JUNO_ENDPOINT": "https://tools-staging.cosmos.azure.com",
|
||||
"isTerminalEnabled" : true,
|
||||
"isPhoenixEnabled" : true
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.31299 1.26164C7.69849 0.897163 8.30151 0.897163 8.68701 1.26164L13.5305 5.84098C13.8302 6.12431 14 6.51853 14 6.93094V12.5002C14 13.3286 13.3284 14.0002 12.5 14.0002H10.5C9.67157 14.0002 9 13.3286 9 12.5002V10.0002C9 9.72407 8.77614 9.50021 8.5 9.50021H7.5C7.22386 9.50021 7 9.72407 7 10.0002V12.5002C7 13.3286 6.32843 14.0002 5.5 14.0002H3.5C2.67157 14.0002 2 13.3286 2 12.5002V6.93094C2 6.51853 2.1698 6.12431 2.46948 5.84098L7.31299 1.26164ZM8 1.98828L3.15649 6.56762C3.0566 6.66207 3 6.79347 3 6.93094V12.5002C3 12.7763 3.22386 13.0002 3.5 13.0002H5.5C5.77614 13.0002 6 12.7763 6 12.5002V10.0002C6 9.17179 6.67157 8.50022 7.5 8.50022H8.5C9.32843 8.50022 10 9.17179 10 10.0002V12.5002C10 12.7763 10.2239 13.0002 10.5 13.0002H12.5C12.7761 13.0002 13 12.7763 13 12.5002V6.93094C13 6.79347 12.9434 6.66207 12.8435 6.56762L8 1.98828Z" fill="#0078D4" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 968 B |
Binary file not shown.
|
Before Width: | Height: | Size: 160 B |
Binary file not shown.
|
Before Width: | Height: | Size: 148 B |
Binary file not shown.
|
Before Width: | Height: | Size: 201 B |
Binary file not shown.
|
Before Width: | Height: | Size: 158 B |
Binary file not shown.
|
Before Width: | Height: | Size: 146 B |
@@ -147,7 +147,6 @@
|
||||
|
||||
// CommandBar
|
||||
@CommandBarButtonHeight: 40px;
|
||||
@FabricCommandBarButtonHeight: 34px;
|
||||
|
||||
/**********************************************************************************
|
||||
Portal Consts
|
||||
@@ -163,10 +162,9 @@
|
||||
/**********************************************************************************/
|
||||
|
||||
@FabricFont: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
|
||||
@FabricToolbarIconColor: "brightness(0) saturate(100%) invert(50%) sepia(17%) saturate(1459%) hue-rotate(81deg) brightness(99%) contrast(94%)";
|
||||
|
||||
@FabricBoxBorderRadius: 8px;
|
||||
@FabricBoxBorderShadow: rgba(0, 0, 0, 0.133) 0px 1.6px 3.6px 0px, rgba(0, 0, 0, 0.11) 0px 0.3px 0.9px 0px;
|
||||
@FabricBoxBorderShadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.14);
|
||||
@FabricBoxMargin: 4px 3px 4px 3px;
|
||||
|
||||
@FabricAccentMediumHigh: #0c695a;
|
||||
|
||||
@@ -2897,21 +2897,9 @@ a:link {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.settingsSectionInlineCheckbox {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.settingsSectionLabel {
|
||||
margin-bottom: @DefaultSpace;
|
||||
margin-right: 5px;
|
||||
|
||||
.panelInfoIcon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.pageOptionsPart {
|
||||
|
||||
@@ -25,38 +25,33 @@ a:focus {
|
||||
}
|
||||
|
||||
.resourceTreeAndTabs {
|
||||
border-radius: 0px;
|
||||
border-radius: @FabricBoxBorderRadius;
|
||||
box-shadow: @FabricBoxBorderShadow;
|
||||
margin: @FabricBoxMargin;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 4px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.tabsManagerContainer {
|
||||
background-color: #ffffff
|
||||
background-color: #fafafa
|
||||
}
|
||||
|
||||
.nav-tabs-margin {
|
||||
padding-top: 8px;
|
||||
background-color: #ffffff
|
||||
background-color: #fafafa
|
||||
}
|
||||
|
||||
.commandBarContainer {
|
||||
background-color: #ffffff;
|
||||
border-radius: @FabricBoxBorderRadius @FabricBoxBorderRadius 0px 0px;
|
||||
border-bottom: none;
|
||||
border-radius: @FabricBoxBorderRadius;
|
||||
box-shadow: @FabricBoxBorderShadow;
|
||||
margin: @FabricBoxMargin;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding-top: 2px;
|
||||
padding: 0px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.dividerContainer {
|
||||
padding: @SmallSpace 0px @SmallSpace 0px;
|
||||
height: @FabricCommandBarButtonHeight;
|
||||
.flex-display();
|
||||
|
||||
span {
|
||||
@@ -163,10 +158,9 @@ a:focus {
|
||||
|
||||
|
||||
.dataExplorerErrorConsoleContainer {
|
||||
border-radius: 0px 0px @FabricBoxBorderRadius @FabricBoxBorderRadius;
|
||||
border-radius: @FabricBoxBorderRadius;
|
||||
box-shadow: @FabricBoxBorderShadow;
|
||||
margin: @FabricBoxMargin;
|
||||
margin-top: 0px;
|
||||
width: auto;
|
||||
align-self: auto;
|
||||
}
|
||||
|
||||
52164
package-lock.json
generated
52164
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
83
package.json
83
package.json
@@ -5,19 +5,19 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@azure/arm-cosmosdb": "9.1.0",
|
||||
"@azure/cosmos": "4.0.1-beta.2",
|
||||
"@azure/cosmos": "4.0.0",
|
||||
"@azure/cosmos-language-service": "0.0.5",
|
||||
"@azure/identity": "1.2.1",
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
"@azure/msal-browser": "2.14.2",
|
||||
"@babel/plugin-proposal-class-properties": "7.12.1",
|
||||
"@babel/plugin-proposal-decorators": "7.12.12",
|
||||
"@fluentui/react": "8.112.1",
|
||||
"@fluentui/react-components": "9.34.0",
|
||||
"@fluentui/react": "8.14.3",
|
||||
"@fluentui/react-components": "9.32.1",
|
||||
"@jupyterlab/services": "6.0.2",
|
||||
"@jupyterlab/terminal": "3.0.3",
|
||||
"@microsoft/applicationinsights-web": "2.6.1",
|
||||
"@nteract/commutable": "7.5.1",
|
||||
"@nteract/commutable": "7.4.2",
|
||||
"@nteract/connected-components": "6.8.2",
|
||||
"@nteract/core": "15.1.0",
|
||||
"@nteract/data-explorer": "8.0.3",
|
||||
@@ -49,20 +49,20 @@
|
||||
"applicationinsights": "1.8.0",
|
||||
"bootstrap": "3.4.1",
|
||||
"canvas": "file:./canvas",
|
||||
"clean-webpack-plugin": "4.0.0",
|
||||
"clean-webpack-plugin": "3.0.0",
|
||||
"clipboard-copy": "4.0.1",
|
||||
"copy-webpack-plugin": "11.0.0",
|
||||
"copy-webpack-plugin": "9.0.1",
|
||||
"crossroads": "0.12.2",
|
||||
"css-element-queries": "1.1.1",
|
||||
"d3": "6.1.1",
|
||||
"datatables.net-colreorder-dt": "1.7.0",
|
||||
"datatables.net-dt": "1.13.8",
|
||||
"datatables.net-colreorder-dt": "1.5.1",
|
||||
"datatables.net-dt": "1.10.19",
|
||||
"date-fns": "1.29.0",
|
||||
"dayjs": "1.8.19",
|
||||
"dom-to-image": "2.6.0",
|
||||
"dotenv": "8.2.0",
|
||||
"eslint-plugin-jest": "27.4.2",
|
||||
"eslint-plugin-react": "7.33.2",
|
||||
"eslint-plugin-jest": "23.13.2",
|
||||
"eslint-plugin-react": "7.20.0",
|
||||
"hasher": "1.2.0",
|
||||
"html2canvas": "1.0.0-rc.5",
|
||||
"i18next": "19.8.4",
|
||||
@@ -71,15 +71,14 @@
|
||||
"iframe-resizer-react": "1.1.0",
|
||||
"immutable": "4.0.0-rc.12",
|
||||
"is-ci": "2.0.0",
|
||||
"jquery": "3.7.1",
|
||||
"jquery-typeahead": "2.11.1",
|
||||
"jquery-ui-dist": "1.13.2",
|
||||
"jquery": "3.5.1",
|
||||
"jquery-typeahead": "2.10.6",
|
||||
"jquery-ui-dist": "1.12.1",
|
||||
"knockout": "3.5.1",
|
||||
"mkdirp": "1.0.4",
|
||||
"monaco-editor": "0.44.0",
|
||||
"monaco-editor": "0.18.1",
|
||||
"ms": "2.1.3",
|
||||
"p-retry": "4.6.2",
|
||||
"patch-package": "8.0.0",
|
||||
"p-retry": "4.2.0",
|
||||
"plotly.js-cartesian-dist-min": "1.52.3",
|
||||
"post-robot": "10.0.42",
|
||||
"q": "1.5.1",
|
||||
@@ -87,7 +86,7 @@
|
||||
"react-animate-height": "2.0.8",
|
||||
"react-dnd": "14.0.2",
|
||||
"react-dnd-html5-backend": "14.0.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-dom": "16.13.1",
|
||||
"react-hotkeys": "2.0.0",
|
||||
"react-i18next": "11.8.5",
|
||||
"react-notification-system": "0.2.17",
|
||||
@@ -95,12 +94,14 @@
|
||||
"react-splitter-layout": "4.0.0",
|
||||
"react-string-format": "1.0.1",
|
||||
"react-youtube": "9.0.1",
|
||||
"redux": "4.0.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rx-jupyter": "5.5.12",
|
||||
"rxjs": "6.6.3",
|
||||
"sanitize-html": "2.3.3",
|
||||
"styled-components": "5.0.1",
|
||||
"styled-components": "4.3.2",
|
||||
"swr": "0.4.0",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"terser-webpack-plugin": "5.1.4",
|
||||
"underscore": "1.9.1",
|
||||
"utility-types": "3.10.0",
|
||||
"zustand": "3.5.0"
|
||||
@@ -115,14 +116,11 @@
|
||||
"@types/codemirror": "0.0.56",
|
||||
"@types/crossroads": "0.0.30",
|
||||
"@types/d3": "5.9.2",
|
||||
"@types/datatables.net": "1.10.28",
|
||||
"@types/datatables.net-colreorder": "1.4.5",
|
||||
"@types/dom-to-image": "2.6.2",
|
||||
"@types/enzyme": "3.10.7",
|
||||
"@types/enzyme-adapter-react-16": "1.0.6",
|
||||
"@types/hasher": "0.0.31",
|
||||
"@types/jest": "26.0.20",
|
||||
"@types/jquery": "3.5.29",
|
||||
"@types/node": "12.11.1",
|
||||
"@types/post-robot": "10.0.1",
|
||||
"@types/q": "1.5.1",
|
||||
@@ -136,62 +134,61 @@
|
||||
"@types/styled-components": "5.1.1",
|
||||
"@types/underscore": "1.7.36",
|
||||
"@types/youtube-player": "5.5.6",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||
"@typescript-eslint/parser": "6.7.4",
|
||||
"@webpack-cli/serve": "2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "4.22.0",
|
||||
"@typescript-eslint/parser": "4.22.0",
|
||||
"@webpack-cli/serve": "1.5.2",
|
||||
"babel-jest": "24.9.0",
|
||||
"babel-loader": "8.1.0",
|
||||
"buffer": "5.1.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.4.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||
"create-file-webpack": "1.0.2",
|
||||
"css-loader": "6.8.1",
|
||||
"css-loader": "1.0.0",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.5",
|
||||
"enzyme-to-json": "3.6.1",
|
||||
"eslint": "8.50.0",
|
||||
"eslint": "7.8.1",
|
||||
"eslint-cli": "1.1.1",
|
||||
"eslint-plugin-no-null": "1.0.2",
|
||||
"eslint-plugin-prefer-arrow": "1.2.3",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-prefer-arrow": "1.2.2",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"expect-playwright": "0.3.3",
|
||||
"fast-glob": "3.2.5",
|
||||
"file-loader": "2.0.0",
|
||||
"fs-extra": "7.0.0",
|
||||
"html-inline-css-webpack-plugin": "1.11.2",
|
||||
"html-inline-css-webpack-plugin": "1.11.0",
|
||||
"html-loader": "0.5.5",
|
||||
"html-loader-jest": "0.2.1",
|
||||
"html-webpack-plugin": "5.5.3",
|
||||
"html-webpack-plugin": "5.3.2",
|
||||
"jest": "26.6.3",
|
||||
"jest-canvas-mock": "2.3.1",
|
||||
"jest-playwright-preset": "1.5.1",
|
||||
"jest-react-hooks-shallow": "1.5.1",
|
||||
"jest-trx-results-processor": "0.0.7",
|
||||
"less": "3.8.1",
|
||||
"less-loader": "11.1.3",
|
||||
"less-loader": "4.1.0",
|
||||
"less-vars-loader": "1.1.0",
|
||||
"mini-css-extract-plugin": "2.1.0",
|
||||
"monaco-editor-webpack-plugin": "7.1.0",
|
||||
"monaco-editor-webpack-plugin": "1.7.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"playwright": "1.13.0",
|
||||
"prettier": "3.0.3",
|
||||
"process": "0.11.10",
|
||||
"querystring-es3": "0.2.1",
|
||||
"raw-loader": "0.5.1",
|
||||
"react-dev-utils": "11.0.4",
|
||||
"rimraf": "3.0.0",
|
||||
"sinon": "3.2.1",
|
||||
"style-loader": "0.23.0",
|
||||
"ts-loader": "9.2.4",
|
||||
"typedoc": "0.21.5",
|
||||
"typescript": "4.3.5",
|
||||
"url-loader": "4.1.1",
|
||||
"typedoc": "0.20.36",
|
||||
"typescript": "4.3.4",
|
||||
"url-loader": "1.1.1",
|
||||
"wait-on": "4.0.2",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-bundle-analyzer": "4.9.1",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1"
|
||||
"webpack": "5.47.0",
|
||||
"webpack-bundle-analyzer": "4.4.2",
|
||||
"webpack-cli": "4.7.2",
|
||||
"webpack-dev-server": "3.11.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
"start": "webpack serve --mode development",
|
||||
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
|
||||
"build:dataExplorer:ci": "npm run build:ci",
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
diff --git a/node_modules/datatables.net-colreorder/types/types.d.ts b/node_modules/datatables.net-colreorder/types/types.d.ts
|
||||
index e5dc283..1930c2b 100644
|
||||
--- a/node_modules/datatables.net-colreorder/types/types.d.ts
|
||||
+++ b/node_modules/datatables.net-colreorder/types/types.d.ts
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
/// <reference types="jquery" />
|
||||
|
||||
-import DataTables, {Api} from 'datatables.net';
|
||||
+import DataTables, { Api } from 'datatables.net';
|
||||
|
||||
export default DataTables;
|
||||
|
||||
@@ -40,6 +40,8 @@ declare module 'datatables.net' {
|
||||
/**
|
||||
* Create a new ColReorder instance for the target DataTable
|
||||
*/
|
||||
+ // Ignore this error: error TS7013: Construct signature, which lacks return-type annotation, implicitly has an 'any' return type.
|
||||
+ // @ts-ignore
|
||||
new (dt: Api<any>, settings: boolean | ConfigColReorder);
|
||||
|
||||
/**
|
||||
@@ -124,35 +124,7 @@ export enum MongoBackendEndpointType {
|
||||
remote,
|
||||
}
|
||||
|
||||
export enum BackendApi {
|
||||
GenerateToken,
|
||||
}
|
||||
|
||||
export class PortalBackendEndpoints {
|
||||
public static readonly Development: string = "https://localhost:7235";
|
||||
public static readonly Mpac: string = "https://cdb-ms-mpac-pbe.cosmos.azure.com";
|
||||
public static readonly Prod: string = "https://cdb-ms-prod-pbe.cosmos.azure.com";
|
||||
public static readonly Fairfax: string = "https://cdb-ff-prod-pbe.cosmos.azure.us";
|
||||
public static readonly Mooncake: string = "https://cdb-mc-prod-pbe.cosmos.azure.cn";
|
||||
}
|
||||
|
||||
export class MongoProxyEndpoints {
|
||||
public static readonly Development: string = "https://localhost:7238";
|
||||
public static readonly Mpac: string = "https://cdb-ms-mpac-mp.cosmos.azure.com";
|
||||
public static readonly Prod: string = "https://cdb-ms-prod-mp.cosmos.azure.com";
|
||||
public static readonly Fairfax: string = "https://cdb-ff-prod-mp.cosmos.azure.us";
|
||||
public static readonly Mooncake: string = "https://cdb-mc-prod-mp.cosmos.azure.cn";
|
||||
}
|
||||
|
||||
export class CassandraProxyEndpoints {
|
||||
public static readonly Development: string = "https://localhost:7240";
|
||||
public static readonly Mpac: string = "https://cdb-ms-mpac-cp.cosmos.azure.com";
|
||||
public static readonly Prod: string = "https://cdb-ms-prod-cp.cosmos.azure.com";
|
||||
public static readonly Fairfax: string = "https://cdb-ff-prod-cp.cosmos.azure.us";
|
||||
public static readonly Mooncake: string = "https://cdb-mc-prod-cp.cosmos.azure.cn";
|
||||
}
|
||||
|
||||
//TODO: Remove this when new backend is migrated over
|
||||
// TODO: 435619 Add default endpoints per cloud and use regional only when available
|
||||
export class CassandraBackend {
|
||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||
public static readonly guestCreateOrDeleteApi: string = "api/guest/cassandra/createordelete";
|
||||
@@ -164,17 +136,6 @@ export class CassandraBackend {
|
||||
public static readonly guestSchemaApi: string = "api/guest/cassandra/schema";
|
||||
}
|
||||
|
||||
export class CassandraProxyAPIs {
|
||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||
public static readonly connectionStringCreateOrDeleteApi: string = "api/connectionstring/cassandra/createordelete";
|
||||
public static readonly queryApi: string = "api/cassandra";
|
||||
public static readonly connectionStringQueryApi: string = "api/connectionstring/cassandra";
|
||||
public static readonly keysApi: string = "api/cassandra/keys";
|
||||
public static readonly connectionStringKeysApi: string = "api/connectionstring/cassandra/keys";
|
||||
public static readonly schemaApi: string = "api/cassandra/schema";
|
||||
public static readonly connectionStringSchemaApi: string = "api/connectionstring/cassandra/schema";
|
||||
}
|
||||
|
||||
export class Queries {
|
||||
public static CustomPageOption: string = "custom";
|
||||
public static UnlimitedPageOption: string = "unlimited";
|
||||
@@ -184,9 +145,6 @@ export class Queries {
|
||||
public static QueryEditorMinHeightRatio: number = 0.1;
|
||||
public static QueryEditorMaxHeightRatio: number = 0.4;
|
||||
public static readonly DefaultMaxDegreeOfParallelism = 6;
|
||||
public static readonly DefaultRetryAttempts = 9;
|
||||
public static readonly DefaultRetryIntervalInMs = 0;
|
||||
public static readonly DefaultMaxWaitTimeInSeconds = 30;
|
||||
}
|
||||
|
||||
export class SavedQueries {
|
||||
@@ -213,7 +171,6 @@ export class Areas {
|
||||
public static Tab: string = "Tab";
|
||||
public static ShareDialog: string = "Share Access Dialog";
|
||||
public static Notebook: string = "Notebook";
|
||||
public static Copilot: string = "Copilot";
|
||||
}
|
||||
|
||||
export class HttpHeaders {
|
||||
@@ -250,10 +207,6 @@ export class HttpHeaders {
|
||||
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
|
||||
}
|
||||
|
||||
export class ContentType {
|
||||
public static applicationJson: string = "application/json";
|
||||
}
|
||||
|
||||
export class ApiType {
|
||||
// Mapped to hexadecimal values in the backend
|
||||
public static readonly MongoDB: number = 1;
|
||||
@@ -474,12 +427,6 @@ export class JunoEndpoints {
|
||||
public static readonly Stage = "https://tools-staging.cosmos.azure.com";
|
||||
}
|
||||
|
||||
export class PriorityLevel {
|
||||
public static readonly High = "high";
|
||||
public static readonly Low = "low";
|
||||
public static readonly Default = "low";
|
||||
}
|
||||
|
||||
export const QueryCopilotSampleDatabaseId = "CopilotSampleDb";
|
||||
export const QueryCopilotSampleContainerId = "SampleContainer";
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import * as Cosmos from "@azure/cosmos";
|
||||
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
|
||||
import { AuthorizationToken } from "Contracts/MessageTypes";
|
||||
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { PriorityLevel } from "../Common/Constants";
|
||||
import { sendCachedDataMessage } from "Common/MessageHandler";
|
||||
import { AuthorizationToken, MessageTypes } from "Contracts/MessageTypes";
|
||||
import { Platform, configContext } from "../ConfigContext";
|
||||
import { userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
@@ -30,44 +26,12 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
||||
}
|
||||
|
||||
if (configContext.platform === Platform.Fabric) {
|
||||
switch (requestInfo.resourceType) {
|
||||
case Cosmos.ResourceType.conflicts:
|
||||
case Cosmos.ResourceType.container:
|
||||
case Cosmos.ResourceType.sproc:
|
||||
case Cosmos.ResourceType.udf:
|
||||
case Cosmos.ResourceType.trigger:
|
||||
case Cosmos.ResourceType.item:
|
||||
case Cosmos.ResourceType.pkranges:
|
||||
// User resource tokens
|
||||
// TODO userContext.fabricContext.databaseConnectionInfo can be undefined
|
||||
headers[HttpHeaders.msDate] = new Date().toUTCString();
|
||||
const resourceTokens = userContext.fabricContext.databaseConnectionInfo.resourceTokens;
|
||||
checkDatabaseResourceTokensValidity(userContext.fabricContext.databaseConnectionInfo.resourceTokensTimestamp);
|
||||
return getAuthorizationTokenUsingResourceTokens(resourceTokens, requestInfo.path, requestInfo.resourceId);
|
||||
|
||||
case Cosmos.ResourceType.none:
|
||||
case Cosmos.ResourceType.database:
|
||||
case Cosmos.ResourceType.offer:
|
||||
case Cosmos.ResourceType.user:
|
||||
case Cosmos.ResourceType.permission:
|
||||
// For now, these operations aren't used, so fetching the authorization token is commented out.
|
||||
// This provider must return a real token to pass validation by the client, so we return the cached resource token
|
||||
// (which is a valid token, but won't work for these operations).
|
||||
const resourceTokens2 = userContext.fabricContext.databaseConnectionInfo.resourceTokens;
|
||||
return getAuthorizationTokenUsingResourceTokens(resourceTokens2, requestInfo.path, requestInfo.resourceId);
|
||||
|
||||
/* ************** TODO: Uncomment this code if we need to support these operations **************
|
||||
// User master tokens
|
||||
const authorizationToken = await sendCachedDataMessage<AuthorizationToken>(
|
||||
MessageTypes.GetAuthorizationToken,
|
||||
[requestInfo],
|
||||
userContext.fabricContext.connectionId,
|
||||
);
|
||||
console.log("Response from Fabric: ", authorizationToken);
|
||||
headers[HttpHeaders.msDate] = authorizationToken.XDate;
|
||||
return decodeURIComponent(authorizationToken.PrimaryReadWriteToken);
|
||||
***********************************************************************************************/
|
||||
}
|
||||
const authorizationToken = await sendCachedDataMessage<AuthorizationToken>(MessageTypes.GetAuthorizationToken, [
|
||||
requestInfo,
|
||||
]);
|
||||
console.log("Response from Fabric: ", authorizationToken);
|
||||
headers[HttpHeaders.msDate] = authorizationToken.XDate;
|
||||
return authorizationToken.PrimaryReadWriteToken;
|
||||
}
|
||||
|
||||
if (userContext.masterKey) {
|
||||
@@ -85,8 +49,6 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
||||
return decodeURIComponent(result.PrimaryReadWriteToken);
|
||||
};
|
||||
|
||||
// TODO
|
||||
// Need to create separate plugins or change endpoint logic to return the correct write or read endpoint.
|
||||
export const requestPlugin: Cosmos.Plugin<any> = async (requestContext, diagnosticNode, next) => {
|
||||
requestContext.endpoint = new URL(configContext.PROXY_PATH, window.location.href).href;
|
||||
requestContext.headers["x-ms-proxy-target"] = endpoint();
|
||||
@@ -136,63 +98,24 @@ enum SDKSupportedCapabilities {
|
||||
PartitionMerge = 1 << 0,
|
||||
}
|
||||
|
||||
// Client Management
|
||||
|
||||
let _client: Cosmos.CosmosClient;
|
||||
let _readClients: Map<string, Cosmos.CosmosClient> = new Map();
|
||||
|
||||
export enum ClientOperationType {
|
||||
READ,
|
||||
WRITE,
|
||||
}
|
||||
|
||||
export function client(clientOperationType: ClientOperationType): Cosmos.CosmosClient {
|
||||
switch (clientOperationType) {
|
||||
case ClientOperationType.READ:
|
||||
return readClients();
|
||||
case ClientOperationType.WRITE:
|
||||
return writeClient();
|
||||
default:
|
||||
throw new Error("Invalid operation type");
|
||||
}
|
||||
}
|
||||
|
||||
export function writeClient(): Cosmos.CosmosClient {
|
||||
console.log(`Called primary client`);
|
||||
const currentUserContextDocumentEndpoint = userContext?.databaseAccount?.properties?.documentEndpoint;
|
||||
console.log(`Current selected endpoint in userContext: ${currentUserContextDocumentEndpoint}`);
|
||||
|
||||
export function client(): Cosmos.CosmosClient {
|
||||
if (_client) return _client;
|
||||
|
||||
let _defaultHeaders: Cosmos.CosmosHeaders = {};
|
||||
_defaultHeaders["x-ms-cosmos-sdk-supportedcapabilities"] =
|
||||
SDKSupportedCapabilities.None | SDKSupportedCapabilities.PartitionMerge;
|
||||
|
||||
if (
|
||||
userContext.authType === AuthType.ConnectionString ||
|
||||
userContext.authType === AuthType.EncryptedToken ||
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
) {
|
||||
// Default to low priority. Needed for non-AAD-auth scenarios
|
||||
// where we cannot use RP API, and thus, cannot detect whether priority
|
||||
// based execution is enabled.
|
||||
// The header will be ignored if priority based execution is disabled on the account.
|
||||
_defaultHeaders["x-ms-cosmos-priority-level"] = PriorityLevel.Default;
|
||||
}
|
||||
|
||||
const options: Cosmos.CosmosClientOptions = {
|
||||
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
|
||||
key: userContext.masterKey,
|
||||
tokenProvider,
|
||||
connectionPolicy: {
|
||||
enableEndpointDiscovery: false,
|
||||
},
|
||||
userAgentSuffix: "Azure Portal",
|
||||
defaultHeaders: _defaultHeaders,
|
||||
connectionPolicy: {
|
||||
retryOptions: {
|
||||
maxRetryAttemptCount: LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts),
|
||||
fixedRetryIntervalInMilliseconds: LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval),
|
||||
maxWaitTimeInSeconds: LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (configContext.PROXY_PATH !== undefined) {
|
||||
@@ -208,61 +131,3 @@ export function writeClient(): Cosmos.CosmosClient {
|
||||
_client = new Cosmos.CosmosClient(options);
|
||||
return _client;
|
||||
}
|
||||
|
||||
export function readClients(): Cosmos.CosmosClient {
|
||||
console.log(`Called read only client`);
|
||||
const currentUserContextDocumentEndpoint = userContext?.databaseAccount?.properties?.documentEndpoint;
|
||||
console.log(`Current selected read endpoint in userContext: ${currentUserContextDocumentEndpoint}`);
|
||||
3;
|
||||
|
||||
const selectedEndpoint = endpoint() || "https://cosmos.azure.com";
|
||||
|
||||
if (_readClients.has(selectedEndpoint)) {
|
||||
return _readClients.get(selectedEndpoint);
|
||||
}
|
||||
|
||||
let _defaultHeaders: Cosmos.CosmosHeaders = {};
|
||||
_defaultHeaders["x-ms-cosmos-sdk-supportedcapabilities"] =
|
||||
SDKSupportedCapabilities.None | SDKSupportedCapabilities.PartitionMerge;
|
||||
|
||||
if (
|
||||
userContext.authType === AuthType.ConnectionString ||
|
||||
userContext.authType === AuthType.EncryptedToken ||
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
) {
|
||||
// Default to low priority. Needed for non-AAD-auth scenarios
|
||||
// where we cannot use RP API, and thus, cannot detect whether priority
|
||||
// based execution is enabled.
|
||||
// The header will be ignored if priority based execution is disabled on the account.
|
||||
_defaultHeaders["x-ms-cosmos-priority-level"] = PriorityLevel.Default;
|
||||
}
|
||||
|
||||
const options: Cosmos.CosmosClientOptions = {
|
||||
endpoint: selectedEndpoint, // CosmosClient gets upset if we pass a bad URL. This should never actually get called
|
||||
key: userContext.masterKey,
|
||||
tokenProvider,
|
||||
userAgentSuffix: "Azure Portal",
|
||||
defaultHeaders: _defaultHeaders,
|
||||
connectionPolicy: {
|
||||
retryOptions: {
|
||||
maxRetryAttemptCount: LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts),
|
||||
fixedRetryIntervalInMilliseconds: LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval),
|
||||
maxWaitTimeInSeconds: LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (configContext.PROXY_PATH !== undefined) {
|
||||
(options as any).plugins = [{ on: "request", plugin: requestPlugin }];
|
||||
}
|
||||
|
||||
if (PriorityBasedExecutionUtils.isFeatureEnabled()) {
|
||||
const plugins = (options as any).plugins || [];
|
||||
plugins.push({ on: "request", plugin: PriorityBasedExecutionUtils.requestPlugin });
|
||||
(options as any).plugins = plugins;
|
||||
}
|
||||
|
||||
_readClients.set(selectedEndpoint, new Cosmos.CosmosClient(options));
|
||||
|
||||
return _readClients.get(selectedEndpoint);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { QueryOperationOptions } from "@azure/cosmos";
|
||||
import { QueryResults } from "../Contracts/ViewModels";
|
||||
|
||||
interface QueryResponse {
|
||||
@@ -11,17 +10,13 @@ interface QueryResponse {
|
||||
}
|
||||
|
||||
export interface MinimalQueryIterator {
|
||||
fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>;
|
||||
fetchNext: () => Promise<QueryResponse>;
|
||||
}
|
||||
|
||||
// Pick<QueryIterator<any>, "fetchNext">;
|
||||
|
||||
export function nextPage(
|
||||
documentsIterator: MinimalQueryIterator,
|
||||
firstItemIndex: number,
|
||||
queryOperationOptions?: QueryOperationOptions,
|
||||
): Promise<QueryResults> {
|
||||
return documentsIterator.fetchNext(queryOperationOptions).then((response) => {
|
||||
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
||||
return documentsIterator.fetchNext().then((response) => {
|
||||
const documents = response.resources;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
jest.mock("./MessageHandler");
|
||||
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
||||
import * as Logger from "./Logger";
|
||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||
import { sendMessage } from "./MessageHandler";
|
||||
|
||||
describe("Logger", () => {
|
||||
@@ -9,16 +11,16 @@ describe("Logger", () => {
|
||||
|
||||
it("should log info messages", () => {
|
||||
Logger.logInfo("Test info", "DocDB");
|
||||
expect(sendMessage).toHaveBeenCalled();
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
|
||||
it("should log error messages", () => {
|
||||
Logger.logError("Test error", "DocDB");
|
||||
expect(sendMessage).toHaveBeenCalled();
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
|
||||
it("should log warnings", () => {
|
||||
Logger.logWarning("Test warning", "DocDB");
|
||||
expect(sendMessage).toHaveBeenCalled();
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,24 +27,15 @@ export function handleCachedDataMessage(message: any): void {
|
||||
runGarbageCollector();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param messageType
|
||||
* @param params
|
||||
* @param scope Use this string to identify request Useful to distinguish response from different senders
|
||||
* @param timeoutInMs
|
||||
* @returns
|
||||
*/
|
||||
export function sendCachedDataMessage<TResponseDataModel>(
|
||||
messageType: MessageTypes,
|
||||
params: Object[],
|
||||
scope?: string,
|
||||
timeoutInMs?: number,
|
||||
): Q.Promise<TResponseDataModel> {
|
||||
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
||||
deferred: Q.defer<TResponseDataModel>(),
|
||||
startTime: new Date(),
|
||||
id: _.uniqueId(scope),
|
||||
id: _.uniqueId(),
|
||||
};
|
||||
RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
||||
sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
||||
@@ -56,10 +47,6 @@ export function sendCachedDataMessage<TResponseDataModel>(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data Overwrite the data property of the message
|
||||
*/
|
||||
export function sendMessage(data: any): void {
|
||||
_sendMessage({
|
||||
signature: "pcIframe",
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
||||
import {
|
||||
allowedMongoProxyEndpoints,
|
||||
allowedMongoProxyEndpoints_ToBeDeprecated,
|
||||
validateEndpoint,
|
||||
} from "Utils/EndpointUtils";
|
||||
import queryString from "querystring";
|
||||
import { allowedMongoProxyEndpoints, validateEndpoint } from "Utils/EndpointValidation";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
@@ -14,7 +10,7 @@ import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import { hasFlag } from "../Platform/Hosted/extractFeatures";
|
||||
import { userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import { ApiType, ContentType, HttpHeaders, HttpStatusCodes, MongoProxyEndpoints } from "./Constants";
|
||||
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
|
||||
import { MinimalQueryIterator } from "./IteratorUtilities";
|
||||
import { sendMessage } from "./MessageHandler";
|
||||
|
||||
@@ -66,73 +62,6 @@ export function queryDocuments(
|
||||
isResourceList: boolean,
|
||||
query: string,
|
||||
continuationToken?: string,
|
||||
): Promise<QueryResponse> {
|
||||
if (!useMongoProxyEndpoint("resourcelist")) {
|
||||
return queryDocuments_ToBeDeprecated(databaseId, collection, isResourceList, query, continuationToken);
|
||||
}
|
||||
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
const params = {
|
||||
databaseID: databaseId,
|
||||
collectionID: collection.id(),
|
||||
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
|
||||
resourceID: collection.rid,
|
||||
resourceType: "docs",
|
||||
subscriptionID: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseAccountName: databaseAccount.name,
|
||||
partitionKey:
|
||||
collection && collection.partitionKey && !collection.partitionKey.systemKey
|
||||
? collection.partitionKeyProperties?.[0]
|
||||
: "",
|
||||
query,
|
||||
};
|
||||
|
||||
const endpoint = getFeatureEndpointOrDefault("resourcelist") || "";
|
||||
|
||||
const headers = {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[CosmosSDKConstants.HttpHeaders.IsQuery]: "true",
|
||||
[CosmosSDKConstants.HttpHeaders.PopulateQueryMetrics]: "true",
|
||||
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
||||
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
||||
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
||||
[HttpHeaders.contentType]: "application/query+json",
|
||||
};
|
||||
|
||||
if (continuationToken) {
|
||||
headers[CosmosSDKConstants.HttpHeaders.Continuation] = continuationToken;
|
||||
}
|
||||
|
||||
const path = isResourceList ? "/resourcelist" : "";
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}${path}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
headers,
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return {
|
||||
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
|
||||
documents: (await response.json()).Documents as DataModels.DocumentId[],
|
||||
headers: response.headers,
|
||||
};
|
||||
}
|
||||
await errorHandling(response, "querying documents", params);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function queryDocuments_ToBeDeprecated(
|
||||
databaseId: string,
|
||||
collection: Collection,
|
||||
isResourceList: boolean,
|
||||
query: string,
|
||||
continuationToken?: string,
|
||||
): Promise<QueryResponse> {
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
@@ -193,54 +122,6 @@ export function readDocument(
|
||||
databaseId: string,
|
||||
collection: Collection,
|
||||
documentId: DocumentId,
|
||||
): Promise<DataModels.DocumentId> {
|
||||
if (!useMongoProxyEndpoint("readDocument")) {
|
||||
return readDocument_ToBeDeprecated(databaseId, collection, documentId);
|
||||
}
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
const idComponents = documentId.self.split("/");
|
||||
const path = idComponents.slice(0, 4).join("/");
|
||||
const rid = encodeURIComponent(idComponents[5]);
|
||||
const params = {
|
||||
databaseID: databaseId,
|
||||
collectionID: collection.id(),
|
||||
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
||||
resourceID: rid,
|
||||
resourceType: "docs",
|
||||
subscriptionID: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseAccountName: databaseAccount.name,
|
||||
partitionKey:
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||
? documentId.partitionKeyProperties?.[0]
|
||||
: "",
|
||||
};
|
||||
|
||||
const endpoint = getFeatureEndpointOrDefault("readDocument");
|
||||
|
||||
return window
|
||||
.fetch(endpoint, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "reading document", params);
|
||||
});
|
||||
}
|
||||
|
||||
export function readDocument_ToBeDeprecated(
|
||||
databaseId: string,
|
||||
collection: Collection,
|
||||
documentId: DocumentId,
|
||||
): Promise<DataModels.DocumentId> {
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
@@ -288,51 +169,6 @@ export function createDocument(
|
||||
collection: Collection,
|
||||
partitionKeyProperty: string,
|
||||
documentContent: unknown,
|
||||
): Promise<DataModels.DocumentId> {
|
||||
if (!useMongoProxyEndpoint("createDocument")) {
|
||||
return createDocument_ToBeDeprecated(databaseId, collection, partitionKeyProperty, documentContent);
|
||||
}
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
const params = {
|
||||
databaseID: databaseId,
|
||||
collectionID: collection.id(),
|
||||
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
|
||||
resourceID: collection.rid,
|
||||
resourceType: "docs",
|
||||
subscriptionID: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseAccountName: databaseAccount.name,
|
||||
partitionKey:
|
||||
collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||
documentContent: JSON.stringify(documentContent),
|
||||
};
|
||||
|
||||
const endpoint = getFeatureEndpointOrDefault("createDocument");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}/createDocument`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "creating document", params);
|
||||
});
|
||||
}
|
||||
|
||||
export function createDocument_ToBeDeprecated(
|
||||
databaseId: string,
|
||||
collection: Collection,
|
||||
partitionKeyProperty: string,
|
||||
documentContent: unknown,
|
||||
): Promise<DataModels.DocumentId> {
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
@@ -372,56 +208,6 @@ export function updateDocument(
|
||||
collection: Collection,
|
||||
documentId: DocumentId,
|
||||
documentContent: string,
|
||||
): Promise<DataModels.DocumentId> {
|
||||
if (!useMongoProxyEndpoint("updateDocument")) {
|
||||
return updateDocument_ToBeDeprecated(databaseId, collection, documentId, documentContent);
|
||||
}
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
const idComponents = documentId.self.split("/");
|
||||
const path = idComponents.slice(0, 5).join("/");
|
||||
const rid = encodeURIComponent(idComponents[5]);
|
||||
const params = {
|
||||
databaseID: databaseId,
|
||||
collectionID: collection.id(),
|
||||
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
||||
resourceID: rid,
|
||||
resourceType: "docs",
|
||||
subscriptionID: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseAccountName: databaseAccount.name,
|
||||
partitionKey:
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||
? documentId.partitionKeyProperties?.[0]
|
||||
: "",
|
||||
documentContent,
|
||||
};
|
||||
const endpoint = getFeatureEndpointOrDefault("updateDocument");
|
||||
|
||||
return window
|
||||
.fetch(endpoint, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(params),
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "updating document", params);
|
||||
});
|
||||
}
|
||||
|
||||
export function updateDocument_ToBeDeprecated(
|
||||
databaseId: string,
|
||||
collection: Collection,
|
||||
documentId: DocumentId,
|
||||
documentContent: string,
|
||||
): Promise<DataModels.DocumentId> {
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
@@ -451,7 +237,7 @@ export function updateDocument_ToBeDeprecated(
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
[HttpHeaders.contentType]: "application/json",
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
})
|
||||
@@ -464,53 +250,6 @@ export function updateDocument_ToBeDeprecated(
|
||||
}
|
||||
|
||||
export function deleteDocument(databaseId: string, collection: Collection, documentId: DocumentId): Promise<void> {
|
||||
if (!useMongoProxyEndpoint("deleteDocument")) {
|
||||
return deleteDocument_ToBeDeprecated(databaseId, collection, documentId);
|
||||
}
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
const idComponents = documentId.self.split("/");
|
||||
const path = idComponents.slice(0, 5).join("/");
|
||||
const rid = encodeURIComponent(idComponents[5]);
|
||||
const params = {
|
||||
databaseID: databaseId,
|
||||
collectionID: collection.id(),
|
||||
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
||||
resourceID: rid,
|
||||
resourceType: "docs",
|
||||
subscriptionID: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseAccountName: databaseAccount.name,
|
||||
partitionKey:
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||
? documentId.partitionKeyProperties?.[0]
|
||||
: "",
|
||||
};
|
||||
const endpoint = getFeatureEndpointOrDefault("deleteDocument");
|
||||
|
||||
return window
|
||||
.fetch(endpoint, {
|
||||
method: "DELETE",
|
||||
body: JSON.stringify(params),
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return undefined;
|
||||
}
|
||||
return await errorHandling(response, "deleting document", params);
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteDocument_ToBeDeprecated(
|
||||
databaseId: string,
|
||||
collection: Collection,
|
||||
documentId: DocumentId,
|
||||
): Promise<void> {
|
||||
const { databaseAccount } = userContext;
|
||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||
const idComponents = documentId.self.split("/");
|
||||
@@ -538,7 +277,7 @@ export function deleteDocument_ToBeDeprecated(
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
[HttpHeaders.contentType]: "application/json",
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
})
|
||||
@@ -552,52 +291,6 @@ export function deleteDocument_ToBeDeprecated(
|
||||
|
||||
export function createMongoCollectionWithProxy(
|
||||
params: DataModels.CreateCollectionParams,
|
||||
): Promise<DataModels.Collection> {
|
||||
if (!useMongoProxyEndpoint("createCollectionWithProxy")) {
|
||||
return createMongoCollectionWithProxy_ToBeDeprecated(params);
|
||||
}
|
||||
const { databaseAccount } = userContext;
|
||||
const shardKey: string = params.partitionKey?.paths[0];
|
||||
|
||||
const createCollectionParams = {
|
||||
databaseID: params.databaseId,
|
||||
collectionID: params.collectionId,
|
||||
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
||||
resourceID: "",
|
||||
resourceType: "colls",
|
||||
subscriptionID: userContext.subscriptionId,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseAccountName: databaseAccount.name,
|
||||
partitionKey: shardKey,
|
||||
isAutoscale: !!params.autoPilotMaxThroughput,
|
||||
hasSharedThroughput: params.databaseLevelThroughput,
|
||||
offerThroughput: params.autoPilotMaxThroughput || params.offerThroughput,
|
||||
createDatabase: params.createNewDatabase,
|
||||
isSharded: !!shardKey,
|
||||
};
|
||||
|
||||
const endpoint = getFeatureEndpointOrDefault("createCollectionWithProxy");
|
||||
|
||||
return window
|
||||
.fetch(`${endpoint}/createCollection`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(createCollectionParams),
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: ContentType.applicationJson,
|
||||
},
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
return await errorHandling(response, "creating collection", createCollectionParams);
|
||||
});
|
||||
}
|
||||
|
||||
export function createMongoCollectionWithProxy_ToBeDeprecated(
|
||||
params: DataModels.CreateCollectionParams,
|
||||
): Promise<DataModels.Collection> {
|
||||
const { databaseAccount } = userContext;
|
||||
const shardKey: string = params.partitionKey?.paths[0];
|
||||
@@ -641,20 +334,13 @@ export function createMongoCollectionWithProxy_ToBeDeprecated(
|
||||
return await errorHandling(response, "creating collection", mongoParams);
|
||||
});
|
||||
}
|
||||
|
||||
export function getFeatureEndpointOrDefault(feature: string): string {
|
||||
let endpoint;
|
||||
if (useMongoProxyEndpoint(feature)) {
|
||||
endpoint = configContext.MONGO_PROXY_ENDPOINT;
|
||||
} else {
|
||||
endpoint =
|
||||
hasFlag(userContext.features.mongoProxyAPIs, feature) &&
|
||||
validateEndpoint(userContext.features.mongoProxyEndpoint, [
|
||||
...allowedMongoProxyEndpoints,
|
||||
...allowedMongoProxyEndpoints_ToBeDeprecated,
|
||||
])
|
||||
? userContext.features.mongoProxyEndpoint
|
||||
: configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
|
||||
}
|
||||
const endpoint =
|
||||
hasFlag(userContext.features.mongoProxyAPIs, feature) &&
|
||||
validateEndpoint(userContext.features.mongoProxyEndpoint, allowedMongoProxyEndpoints)
|
||||
? userContext.features.mongoProxyEndpoint
|
||||
: configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
|
||||
|
||||
return getEndpoint(endpoint);
|
||||
}
|
||||
@@ -663,11 +349,7 @@ export function getEndpoint(endpoint: string): string {
|
||||
let url = endpoint + "/api/mongo/explorer";
|
||||
|
||||
if (userContext.authType === AuthType.EncryptedToken) {
|
||||
if (endpoint === configContext.MONGO_PROXY_ENDPOINT) {
|
||||
url = url.replace("api/mongo", "api/connectionstring/mongo");
|
||||
} else {
|
||||
url = url.replace("api/mongo", "api/guest/mongo");
|
||||
}
|
||||
url = url.replace("api/mongo", "api/guest/mongo");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -688,17 +370,3 @@ async function errorHandling(response: Response, action: string, params: unknown
|
||||
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string {
|
||||
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/mongodbDatabases/${params.db}/collections/${params.coll}`;
|
||||
}
|
||||
|
||||
function useMongoProxyEndpoint(api: string): boolean {
|
||||
const activeMongoProxyEndpoints: string[] = [MongoProxyEndpoints.Development];
|
||||
let canAccessMongoProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
|
||||
if (userContext.databaseAccount.properties.ipRules?.length > 0) {
|
||||
canAccessMongoProxy = canAccessMongoProxy && configContext.MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED;
|
||||
}
|
||||
|
||||
return (
|
||||
canAccessMongoProxy &&
|
||||
configContext.NEW_MONGO_APIS?.includes(api) &&
|
||||
activeMongoProxyEndpoints.includes(configContext.MONGO_PROXY_ENDPOINT)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||
import * as _ from "underscore";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
@@ -9,11 +8,12 @@ import { useDatabases } from "../Explorer/useDatabases";
|
||||
import { userContext } from "../UserContext";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
import { createCollection } from "./dataAccess/createCollection";
|
||||
import { createDocument } from "./dataAccess/createDocument";
|
||||
import { deleteDocument } from "./dataAccess/deleteDocument";
|
||||
import { queryDocuments } from "./dataAccess/queryDocuments";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||
|
||||
export class QueriesClient {
|
||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||
|
||||
@@ -146,7 +146,6 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
onClick={onEditEntity}
|
||||
tabIndex={0}
|
||||
onKeyPress={handleKeyPress}
|
||||
role="button"
|
||||
/>
|
||||
</div>
|
||||
</TooltipHost>
|
||||
@@ -161,7 +160,6 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
onClick={onDeleteEntity}
|
||||
tabIndex={0}
|
||||
onKeyPress={handleKeyPressdelete}
|
||||
role="button"
|
||||
/>
|
||||
</TooltipHost>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { JSONObject, OperationResponse } from "@azure/cosmos";
|
||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export const bulkCreateDocument = async (
|
||||
@@ -13,7 +13,7 @@ export const bulkCreateDocument = async (
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.items.bulk(
|
||||
|
||||
@@ -6,14 +6,14 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getCollectionName } from "../../Utils/APITypeUtils";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { createUpdateCassandraTable } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
||||
import { createUpdateGremlinGraph } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
||||
import { createUpdateMongoDBCollection } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||
import { createUpdateSqlContainer } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { createUpdateTable } from "../../Utils/arm/generatedClients/cosmos/tableResources";
|
||||
import * as ARMTypes from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { createMongoCollectionWithProxy } from "../MongoProxyClient";
|
||||
import { createDatabase } from "./createDatabase";
|
||||
@@ -284,9 +284,7 @@ const createCollectionWithSDK = async (params: DataModels.CreateCollectionParams
|
||||
}
|
||||
}
|
||||
|
||||
const databaseResponse: DatabaseResponse = await client(ClientOperationType.WRITE).databases.createIfNotExists(
|
||||
createDatabaseBody,
|
||||
);
|
||||
const databaseResponse: DatabaseResponse = await client().databases.createIfNotExists(createDatabaseBody);
|
||||
const collectionResponse: ContainerResponse = await databaseResponse?.database.containers.create(
|
||||
createCollectionBody,
|
||||
collectionOptions,
|
||||
|
||||
@@ -4,7 +4,6 @@ import * as DataModels from "../../Contracts/DataModels";
|
||||
import { useDatabases } from "../../Explorer/useDatabases";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getDatabaseName } from "../../Utils/APITypeUtils";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { createUpdateCassandraKeyspace } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
||||
import { createUpdateGremlinDatabase } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
||||
import { createUpdateMongoDBDatabase } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||
@@ -16,7 +15,8 @@ import {
|
||||
MongoDBDatabaseCreateUpdateParameters,
|
||||
SqlDatabaseCreateUpdateParameters,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
||||
@@ -153,7 +153,7 @@ async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): P
|
||||
}
|
||||
}
|
||||
|
||||
const response: DatabaseResponse = await client(ClientOperationType.WRITE).databases.create(createBody);
|
||||
const response: DatabaseResponse = await client().databases.create(createBody);
|
||||
return response.resource;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
@@ -9,7 +9,7 @@ export const createDocument = async (collection: CollectionBase, newDocument: un
|
||||
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
|
||||
|
||||
try {
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.items.create(newDocument);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
createUpdateSqlStoredProcedure,
|
||||
getSqlStoredProcedure,
|
||||
@@ -10,7 +9,8 @@ import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createStoredProcedure(
|
||||
@@ -63,7 +63,7 @@ export async function createStoredProcedure(
|
||||
return rpResponse && (rpResponse.properties?.resource as StoredProcedureDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.storedProcedures.create(storedProcedure);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { SqlTriggerCreateUpdateParameters, SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createTrigger(
|
||||
@@ -55,7 +55,7 @@ export async function createTrigger(
|
||||
return rpResponse && rpResponse.properties?.resource;
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.triggers.create(trigger as unknown as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
createUpdateSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction,
|
||||
@@ -10,7 +9,8 @@ import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function createUserDefinedFunction(
|
||||
@@ -63,7 +63,7 @@ export async function createUserDefinedFunction(
|
||||
return rpResponse && (rpResponse.properties?.resource as UserDefinedFunctionDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.userDefinedFunctions.create(userDefinedFunction);
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
import { ApiType, userContext } from "UserContext";
|
||||
import * as NotificationConsoleUtils from "Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
cancel,
|
||||
create,
|
||||
get,
|
||||
listByDatabaseAccount,
|
||||
} from "Utils/arm/generatedClients/dataTransferService/dataTransferJobs";
|
||||
import {
|
||||
CosmosCassandraDataTransferDataSourceSink,
|
||||
CosmosMongoDataTransferDataSourceSink,
|
||||
CosmosSqlDataTransferDataSourceSink,
|
||||
CreateJobRequest,
|
||||
DataTransferJobFeedResults,
|
||||
DataTransferJobGetResults,
|
||||
} from "Utils/arm/generatedClients/dataTransferService/types";
|
||||
import { addToPolling, removeFromPolling, updateDataTransferJob, useDataTransferJobs } from "hooks/useDataTransferJobs";
|
||||
import promiseRetry, { AbortError, FailedAttemptError } from "p-retry";
|
||||
|
||||
export interface DataTransferParams {
|
||||
jobName: string;
|
||||
apiType: ApiType;
|
||||
subscriptionId: string;
|
||||
resourceGroupName: string;
|
||||
accountName: string;
|
||||
sourceDatabaseName: string;
|
||||
sourceCollectionName: string;
|
||||
targetDatabaseName: string;
|
||||
targetCollectionName: string;
|
||||
}
|
||||
|
||||
export const getDataTransferJobs = async (
|
||||
subscriptionId: string,
|
||||
resourceGroup: string,
|
||||
accountName: string,
|
||||
): Promise<DataTransferJobGetResults[]> => {
|
||||
let dataTransferJobs: DataTransferJobGetResults[] = [];
|
||||
let dataTransferFeeds: DataTransferJobFeedResults = await listByDatabaseAccount(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
accountName,
|
||||
);
|
||||
dataTransferJobs = [...dataTransferJobs, ...(dataTransferFeeds?.value || [])];
|
||||
while (dataTransferFeeds?.nextLink) {
|
||||
const nextResponse = await window.fetch(dataTransferFeeds.nextLink, {
|
||||
headers: {
|
||||
Authorization: userContext.authorizationToken,
|
||||
},
|
||||
});
|
||||
if (nextResponse.ok) {
|
||||
dataTransferFeeds = await nextResponse.json();
|
||||
dataTransferJobs = [...dataTransferJobs, ...(dataTransferFeeds?.value || [])];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dataTransferJobs;
|
||||
};
|
||||
|
||||
export const initiateDataTransfer = async (params: DataTransferParams): Promise<DataTransferJobGetResults> => {
|
||||
const {
|
||||
jobName,
|
||||
apiType,
|
||||
subscriptionId,
|
||||
resourceGroupName,
|
||||
accountName,
|
||||
sourceDatabaseName,
|
||||
sourceCollectionName,
|
||||
targetDatabaseName,
|
||||
targetCollectionName,
|
||||
} = params;
|
||||
const sourcePayload = createPayload(apiType, sourceDatabaseName, sourceCollectionName);
|
||||
const targetPayload = createPayload(apiType, targetDatabaseName, targetCollectionName);
|
||||
const body: CreateJobRequest = {
|
||||
properties: {
|
||||
source: sourcePayload,
|
||||
destination: targetPayload,
|
||||
},
|
||||
};
|
||||
return create(subscriptionId, resourceGroupName, accountName, jobName, body);
|
||||
};
|
||||
|
||||
export const pollDataTransferJob = async (
|
||||
jobName: string,
|
||||
subscriptionId: string,
|
||||
resourceGroupName: string,
|
||||
accountName: string,
|
||||
): Promise<unknown> => {
|
||||
const currentPollingJobs = useDataTransferJobs.getState().pollingDataTransferJobs;
|
||||
if (currentPollingJobs.has(jobName)) {
|
||||
return;
|
||||
}
|
||||
let clearMessage = NotificationConsoleUtils.logConsoleProgress(`Data transfer job ${jobName} in progress`);
|
||||
return await promiseRetry(
|
||||
() => pollDataTransferJobOperation(jobName, subscriptionId, resourceGroupName, accountName, clearMessage),
|
||||
{
|
||||
retries: 500,
|
||||
maxTimeout: 5000,
|
||||
onFailedAttempt: (error: FailedAttemptError) => {
|
||||
clearMessage();
|
||||
clearMessage = NotificationConsoleUtils.logConsoleProgress(error.message);
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const pollDataTransferJobOperation = async (
|
||||
jobName: string,
|
||||
subscriptionId: string,
|
||||
resourceGroupName: string,
|
||||
accountName: string,
|
||||
clearMessage?: () => void,
|
||||
): Promise<DataTransferJobGetResults> => {
|
||||
if (!userContext.authorizationToken) {
|
||||
throw new Error("No authority token provided");
|
||||
}
|
||||
|
||||
addToPolling(jobName);
|
||||
|
||||
const body: DataTransferJobGetResults = await get(subscriptionId, resourceGroupName, accountName, jobName);
|
||||
const status = body?.properties?.status;
|
||||
|
||||
updateDataTransferJob(body);
|
||||
|
||||
if (status === "Cancelled") {
|
||||
removeFromPolling(jobName);
|
||||
clearMessage && clearMessage();
|
||||
const cancelMessage = `Data transfer job ${jobName} cancelled`;
|
||||
NotificationConsoleUtils.logConsoleError(cancelMessage);
|
||||
throw new AbortError(cancelMessage);
|
||||
}
|
||||
if (status === "Failed" || status === "Faulted") {
|
||||
removeFromPolling(jobName);
|
||||
const errorMessage = body?.properties?.error
|
||||
? JSON.stringify(body?.properties?.error)
|
||||
: "Operation could not be completed";
|
||||
const error = new Error(errorMessage);
|
||||
clearMessage && clearMessage();
|
||||
NotificationConsoleUtils.logConsoleError(`Data transfer job ${jobName} failed: ${errorMessage}`);
|
||||
throw new AbortError(error);
|
||||
}
|
||||
if (status === "Completed") {
|
||||
removeFromPolling(jobName);
|
||||
clearMessage && clearMessage();
|
||||
NotificationConsoleUtils.logConsoleInfo(`Data transfer job ${jobName} completed`);
|
||||
return body;
|
||||
}
|
||||
const processedCount = body.properties.processedCount;
|
||||
const totalCount = body.properties.totalCount;
|
||||
const retryMessage = `Data transfer job ${jobName} in progress, total count: ${totalCount}, processed count: ${processedCount}`;
|
||||
throw new Error(retryMessage);
|
||||
};
|
||||
|
||||
export const cancelDataTransferJob = async (
|
||||
subscriptionId: string,
|
||||
resourceGroupName: string,
|
||||
accountName: string,
|
||||
jobName: string,
|
||||
): Promise<void> => {
|
||||
const cancelResult: DataTransferJobGetResults = await cancel(subscriptionId, resourceGroupName, accountName, jobName);
|
||||
updateDataTransferJob(cancelResult);
|
||||
removeFromPolling(cancelResult?.properties?.jobName);
|
||||
};
|
||||
|
||||
const createPayload = (
|
||||
apiType: ApiType,
|
||||
databaseName: string,
|
||||
containerName: string,
|
||||
):
|
||||
| CosmosSqlDataTransferDataSourceSink
|
||||
| CosmosMongoDataTransferDataSourceSink
|
||||
| CosmosCassandraDataTransferDataSourceSink => {
|
||||
switch (apiType) {
|
||||
case "SQL":
|
||||
return {
|
||||
component: "CosmosDBSql",
|
||||
databaseName: databaseName,
|
||||
containerName: containerName,
|
||||
} as CosmosSqlDataTransferDataSourceSink;
|
||||
case "Mongo":
|
||||
return {
|
||||
component: "CosmosDBMongo",
|
||||
databaseName: databaseName,
|
||||
collectionName: containerName,
|
||||
} as CosmosMongoDataTransferDataSourceSink;
|
||||
case "Cassandra":
|
||||
return {
|
||||
component: "CosmosDBCassandra",
|
||||
keyspaceName: databaseName,
|
||||
tableName: containerName,
|
||||
};
|
||||
default:
|
||||
throw new Error(`Unsupported API type for data transfer: ${apiType}`);
|
||||
}
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import { deleteMongoDBCollection } from "../../Utils/arm/generatedClients/cosmos
|
||||
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { deleteTable } from "../../Utils/arm/generatedClients/cosmos/tableResources";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client, ClientOperationType } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
|
||||
@@ -15,7 +15,7 @@ export async function deleteCollection(databaseId: string, collectionId: string)
|
||||
if (userContext.authType === AuthType.AAD && !userContext.features.enableSDKoperations) {
|
||||
await deleteCollectionWithARM(databaseId, collectionId);
|
||||
} else {
|
||||
await client(ClientOperationType.WRITE).database(databaseId).container(collectionId).delete();
|
||||
await client().database(databaseId).container(collectionId).delete();
|
||||
}
|
||||
logConsoleInfo(`Successfully deleted container ${collectionId}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { RequestOptions } from "@azure/cosmos";
|
||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import ConflictId from "../../Explorer/Tree/ConflictId";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import { RequestOptions } from "@azure/cosmos";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
|
||||
export const deleteConflict = async (collection: CollectionBase, conflictId: ConflictId): Promise<void> => {
|
||||
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
|
||||
@@ -13,7 +13,7 @@ export const deleteConflict = async (collection: CollectionBase, conflictId: Con
|
||||
partitionKey: getPartitionKeyHeaderForConflict(conflictId),
|
||||
};
|
||||
|
||||
await client(ClientOperationType.WRITE)
|
||||
await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.conflict(conflictId.id())
|
||||
|
||||
@@ -5,7 +5,7 @@ import { deleteGremlinDatabase } from "../../Utils/arm/generatedClients/cosmos/g
|
||||
import { deleteMongoDBDatabase } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client, ClientOperationType } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteDatabase(databaseId: string): Promise<void> {
|
||||
@@ -15,7 +15,7 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.features.enableSDKoperations) {
|
||||
await deleteDatabaseWithARM(databaseId);
|
||||
} else {
|
||||
await client(ClientOperationType.WRITE).database(databaseId).delete();
|
||||
await client().database(databaseId).delete();
|
||||
}
|
||||
logConsoleInfo(`Successfully deleted database ${databaseId}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import DocumentId from "../../Explorer/Tree/DocumentId";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||
@@ -11,7 +11,7 @@ export const deleteDocument = async (collection: CollectionBase, documentId: Doc
|
||||
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
|
||||
|
||||
try {
|
||||
await client(ClientOperationType.WRITE)
|
||||
await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteSqlStoredProcedure } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client, ClientOperationType } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteStoredProcedure(
|
||||
@@ -26,11 +26,7 @@ export async function deleteStoredProcedure(
|
||||
storedProcedureId,
|
||||
);
|
||||
} else {
|
||||
await client(ClientOperationType.WRITE)
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.storedProcedure(storedProcedureId)
|
||||
.delete();
|
||||
await client().database(databaseId).container(collectionId).scripts.storedProcedure(storedProcedureId).delete();
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "DeleteStoredProcedure", `Error while deleting stored procedure ${storedProcedureId}`);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteSqlTrigger } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client, ClientOperationType } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteTrigger(databaseId: string, collectionId: string, triggerId: string): Promise<void> {
|
||||
@@ -22,11 +22,7 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
|
||||
triggerId,
|
||||
);
|
||||
} else {
|
||||
await client(ClientOperationType.WRITE)
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.trigger(triggerId)
|
||||
.delete();
|
||||
await client().database(databaseId).container(collectionId).scripts.trigger(triggerId).delete();
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "DeleteTrigger", `Error while deleting trigger ${triggerId}`);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { deleteSqlUserDefinedFunction } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client, ClientOperationType } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function deleteUserDefinedFunction(databaseId: string, collectionId: string, id: string): Promise<void> {
|
||||
@@ -22,11 +22,7 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
|
||||
id,
|
||||
);
|
||||
} else {
|
||||
await client(ClientOperationType.WRITE)
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.userDefinedFunction(id)
|
||||
.delete();
|
||||
await client().database(databaseId).container(collectionId).scripts.userDefinedFunction(id).delete();
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "DeleteUserDefinedFunction", `Error while deleting user defined function ${id}`);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Collection } from "../../Contracts/ViewModels";
|
||||
import StoredProcedure from "../../Explorer/Tree/StoredProcedure";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientDefaults, HttpHeaders } from "../Constants";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
import StoredProcedure from "../../Explorer/Tree/StoredProcedure";
|
||||
|
||||
export interface ExecuteSprocResult {
|
||||
result: StoredProcedure;
|
||||
@@ -22,7 +22,7 @@ export const executeStoredProcedure = async (
|
||||
}, ClientDefaults.requestTimeoutMs);
|
||||
|
||||
try {
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.scripts.storedProcedure(storedProcedure.id())
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import * as Constants from "../Constants";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
||||
export async function getIndexTransformationProgress(databaseId: string, collectionId: string): Promise<number> {
|
||||
if (userContext.authType !== AuthType.AAD) {
|
||||
@@ -12,10 +12,7 @@ export async function getIndexTransformationProgress(databaseId: string, collect
|
||||
let indexTransformationPercentage: number;
|
||||
const clearMessage = logConsoleProgress(`Reading container ${collectionId}`);
|
||||
try {
|
||||
const response = await client(ClientOperationType.READ)
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.read({ populateQuotaInfo: true });
|
||||
const response = await client().database(databaseId).container(collectionId).read({ populateQuotaInfo: true });
|
||||
|
||||
indexTransformationPercentage = parseInt(
|
||||
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ConflictDefinition, FeedOptions, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
|
||||
export const queryConflicts = (
|
||||
databaseId: string,
|
||||
@@ -7,5 +7,5 @@ export const queryConflicts = (
|
||||
query: string,
|
||||
options: FeedOptions,
|
||||
): QueryIterator<ConflictDefinition & Resource> => {
|
||||
return client(ClientOperationType.READ).database(databaseId).container(containerId).conflicts.query(query, options);
|
||||
return client().database(databaseId).container(containerId).conflicts.query(query, options);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||
import { getCommonQueryOptions } from "./queryDocuments";
|
||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||
|
||||
describe("getCommonQueryOptions", () => {
|
||||
it("builds the correct default options objects", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||
import { Queries } from "../Constants";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
|
||||
export const queryDocuments = (
|
||||
databaseId: string,
|
||||
@@ -10,7 +10,7 @@ export const queryDocuments = (
|
||||
options: FeedOptions,
|
||||
): QueryIterator<ItemDefinition & Resource> => {
|
||||
options = getCommonQueryOptions(options);
|
||||
return client(ClientOperationType.READ).database(databaseId).container(containerId).items.query(query, options);
|
||||
return client().database(databaseId).container(containerId).items.query(query, options);
|
||||
};
|
||||
|
||||
export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
|
||||
@@ -26,5 +26,6 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
|
||||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
|
||||
Queries.itemsPerPage;
|
||||
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { QueryOperationOptions } from "@azure/cosmos";
|
||||
import { QueryResults } from "../../Contracts/ViewModels";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
@@ -9,13 +8,12 @@ export const queryDocumentsPage = async (
|
||||
resourceName: string,
|
||||
documentsIterator: MinimalQueryIterator,
|
||||
firstItemIndex: number,
|
||||
queryOperationOptions?: QueryOperationOptions,
|
||||
): Promise<QueryResults> => {
|
||||
const entityName = getEntityName();
|
||||
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
|
||||
|
||||
try {
|
||||
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex, queryOperationOptions);
|
||||
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
|
||||
const itemCount = (result.documents && result.documents.length) || 0;
|
||||
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
|
||||
return result;
|
||||
|
||||
@@ -3,11 +3,11 @@ import { sampleDataClient } from "Common/SampleDataClient";
|
||||
import { userContext } from "UserContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readCollection(databaseId: string, collectionId: string): Promise<DataModels.Collection> {
|
||||
const cosmosClient = client(ClientOperationType.READ);
|
||||
const cosmosClient = client();
|
||||
return await readCollectionInternal(cosmosClient, databaseId, collectionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,18 @@
|
||||
import { ContainerResponse } from "@azure/cosmos";
|
||||
import { Queries } from "Common/Constants";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { listCassandraTables } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
||||
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
||||
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||
import { listSqlContainers } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { listTables } from "../../Utils/arm/generatedClients/cosmos/tableResources";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||
|
||||
if (
|
||||
configContext.platform === Platform.Fabric &&
|
||||
userContext.fabricContext &&
|
||||
userContext.fabricContext.databaseConnectionInfo.databaseId === databaseId
|
||||
) {
|
||||
const collections: DataModels.Collection[] = [];
|
||||
const promises: Promise<ContainerResponse>[] = [];
|
||||
|
||||
for (const collectionResourceId in userContext.fabricContext.databaseConnectionInfo.resourceTokens) {
|
||||
// Dictionary key looks like this: dbs/SampleDB/colls/Container
|
||||
const resourceIdObj = collectionResourceId.split("/");
|
||||
const tokenDatabaseId = resourceIdObj[1];
|
||||
const tokenCollectionId = resourceIdObj[3];
|
||||
|
||||
if (tokenDatabaseId === databaseId) {
|
||||
promises.push(client(ClientOperationType.READ).database(databaseId).container(tokenCollectionId).read());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const responses = await Promise.all(promises);
|
||||
responses.forEach((response) => {
|
||||
collections.push(response.resource as DataModels.Collection);
|
||||
});
|
||||
|
||||
// Sort collections by id before returning
|
||||
collections.sort((a, b) => a.id.localeCompare(b.id));
|
||||
return collections;
|
||||
} catch (error) {
|
||||
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
|
||||
throw error;
|
||||
} finally {
|
||||
clearMessage();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
@@ -61,7 +22,7 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
|
||||
return await readCollectionsWithARM(databaseId);
|
||||
}
|
||||
|
||||
const sdkResponse = await client(ClientOperationType.READ).database(databaseId).containers.readAll().fetchAll();
|
||||
const sdkResponse = await client().database(databaseId).containers.readAll().fetchAll();
|
||||
return sdkResponse.resources as DataModels.Collection[];
|
||||
} catch (error) {
|
||||
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
|
||||
@@ -77,7 +38,7 @@ export async function readCollectionsWithPagination(
|
||||
): Promise<DataModels.CollectionsWithPagination> {
|
||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||
try {
|
||||
const sdkResponse = await client(ClientOperationType.READ)
|
||||
const sdkResponse = await client()
|
||||
.database(databaseId)
|
||||
.containers.query(
|
||||
{ query: "SELECT * FROM c" },
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
||||
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
||||
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { readOfferWithSDK } from "./readOfferWithSDK";
|
||||
|
||||
export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promise<Offer> => {
|
||||
if (configContext.platform === Platform.Fabric) {
|
||||
// TODO This works, but is very slow, because it requests the token, so we skip for now
|
||||
console.error("Skiping readDatabaseOffer for Fabric");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const clearMessage = logConsoleProgress(`Querying offer for database ${params.databaseId}`);
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,53 +1,17 @@
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
|
||||
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
|
||||
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
|
||||
import { listSqlDatabases } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readDatabases(): Promise<DataModels.Database[]> {
|
||||
let databases: DataModels.Database[];
|
||||
const clearMessage = logConsoleProgress(`Querying databases`);
|
||||
|
||||
if (configContext.platform === Platform.Fabric && userContext.fabricContext?.databaseConnectionInfo.resourceTokens) {
|
||||
const tokensData = userContext.fabricContext.databaseConnectionInfo;
|
||||
|
||||
const databaseIdsSet = new Set<string>(); // databaseId
|
||||
|
||||
for (const collectionResourceId in tokensData.resourceTokens) {
|
||||
// Dictionary key looks like this: dbs/SampleDB/colls/Container
|
||||
const resourceIdObj = collectionResourceId.split("/");
|
||||
|
||||
if (resourceIdObj.length !== 4) {
|
||||
handleError(`Resource key not recognized: ${resourceIdObj}`, "ReadDatabases", `Error while querying databases`);
|
||||
clearMessage();
|
||||
return [];
|
||||
}
|
||||
|
||||
const databaseId = resourceIdObj[1];
|
||||
|
||||
databaseIdsSet.add(databaseId);
|
||||
}
|
||||
|
||||
const databases: DataModels.Database[] = Array.from(databaseIdsSet.values())
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((databaseId) => ({
|
||||
_rid: "",
|
||||
_self: "",
|
||||
_etag: "",
|
||||
_ts: 0,
|
||||
id: databaseId,
|
||||
collections: [],
|
||||
}));
|
||||
clearMessage();
|
||||
return databases;
|
||||
}
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
@@ -56,7 +20,7 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
|
||||
) {
|
||||
databases = await readDatabasesWithARM();
|
||||
} else {
|
||||
const sdkResponse = await client(ClientOperationType.READ).databases.readAll().fetchAll();
|
||||
const sdkResponse = await client().databases.readAll().fetchAll();
|
||||
databases = sdkResponse.resources as DataModels.Database[];
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import DocumentId from "../../Explorer/Tree/DocumentId";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { HttpHeaders } from "../Constants";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||
@@ -19,7 +19,7 @@ export const readDocument = async (collection: CollectionBase, documentId: Docum
|
||||
[HttpHeaders.partitionKey]: documentId.partitionKeyValue,
|
||||
}
|
||||
: {};
|
||||
const response = await client(ClientOperationType.READ)
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RequestOptions } from "@azure/cosmos";
|
||||
import { Offer } from "../../Contracts/DataModels";
|
||||
import { HttpHeaders } from "../Constants";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { parseSDKOfferResponse } from "../OfferUtility";
|
||||
import { readOffers } from "./readOffers";
|
||||
|
||||
@@ -21,7 +21,7 @@ export const readOfferWithSDK = async (offerId: string, resourceId: string): Pro
|
||||
[HttpHeaders.populateCollectionThroughputInfo]: true,
|
||||
},
|
||||
};
|
||||
const response = await client(ClientOperationType.READ).offer(offerId).read(options);
|
||||
const response = await client().offer(offerId).read(options);
|
||||
|
||||
return parseSDKOfferResponse(response);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { SDKOfferDefinition } from "../../Contracts/DataModels";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { getErrorMessage, handleError } from "../ErrorHandlingUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError, getErrorMessage } from "../ErrorHandlingUtils";
|
||||
|
||||
export const readOffers = async (): Promise<SDKOfferDefinition[]> => {
|
||||
const clearMessage = logConsoleProgress(`Querying offers`);
|
||||
|
||||
try {
|
||||
const response = await client(ClientOperationType.READ).offers.readAll().fetchAll();
|
||||
const response = await client().offers.readAll().fetchAll();
|
||||
return response?.resources;
|
||||
} catch (error) {
|
||||
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readStoredProcedures(
|
||||
@@ -34,7 +34,7 @@ export async function readStoredProcedures(
|
||||
throw new Error(cloudError?.error?.message);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.READ)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.storedProcedures.readAll()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { listSqlTriggers } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readTriggers(
|
||||
@@ -28,11 +28,7 @@ export async function readTriggers(
|
||||
return rpResponse?.value?.map((trigger) => trigger.properties?.resource);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.READ)
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.triggers.readAll()
|
||||
.fetchAll();
|
||||
const response = await client().database(databaseId).container(collectionId).scripts.triggers.readAll().fetchAll();
|
||||
return response?.resources;
|
||||
} catch (error) {
|
||||
handleError(error, "ReadTriggers", `Failed to query triggers for container ${collectionId}`);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { listSqlUserDefinedFunctions } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function readUserDefinedFunctions(
|
||||
@@ -24,7 +24,7 @@ export async function readUserDefinedFunctions(
|
||||
return rpResponse?.value?.map((udf) => udf.properties?.resource as UserDefinedFunctionDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.READ)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.userDefinedFunctions.readAll()
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ContainerDefinition, RequestOptions } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { Collection } from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
createUpdateCassandraTable,
|
||||
getCassandraTable,
|
||||
@@ -20,7 +19,8 @@ import {
|
||||
SqlContainerCreateUpdateParameters,
|
||||
SqlContainerResource,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateCollection(
|
||||
@@ -40,7 +40,7 @@ export async function updateCollection(
|
||||
) {
|
||||
collection = await updateCollectionWithARM(databaseId, collectionId, newCollection);
|
||||
} else {
|
||||
const sdkResponse = await client(ClientOperationType.WRITE)
|
||||
const sdkResponse = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.replace(newCollection as ContainerDefinition, options);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { HttpHeaders } from "Common/Constants";
|
||||
import { CollectionBase } from "../../Contracts/ViewModels";
|
||||
import DocumentId from "../../Explorer/Tree/DocumentId";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||
@@ -23,7 +23,7 @@ export const updateDocument = async (
|
||||
[HttpHeaders.partitionKey]: documentId.partitionKeyValue,
|
||||
}
|
||||
: {};
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||
|
||||
@@ -2,7 +2,6 @@ import { OfferDefinition, RequestOptions } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { Offer, SDKOfferDefinition, UpdateOfferParams } from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
migrateCassandraKeyspaceToAutoscale,
|
||||
migrateCassandraKeyspaceToManualThroughput,
|
||||
@@ -41,8 +40,9 @@ import {
|
||||
updateTableThroughput,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/tableResources";
|
||||
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { HttpHeaders } from "../Constants";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { parseSDKOfferResponse } from "../OfferUtility";
|
||||
import { readCollectionOffer } from "./readCollectionOffer";
|
||||
@@ -401,7 +401,7 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
|
||||
}
|
||||
|
||||
const sdkResponse = await client(ClientOperationType.WRITE)
|
||||
const sdkResponse = await client()
|
||||
.offer(params.currentOffer.id)
|
||||
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
|
||||
.replace(newOffer as unknown as OfferDefinition, options);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
createUpdateSqlStoredProcedure,
|
||||
getSqlStoredProcedure,
|
||||
@@ -10,7 +9,8 @@ import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateStoredProcedure(
|
||||
@@ -54,7 +54,7 @@ export async function updateStoredProcedure(
|
||||
throw new Error(`Failed to update stored procedure: ${storedProcedure.id} does not exist.`);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.storedProcedure(storedProcedure.id)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { SqlTriggerCreateUpdateParameters, SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateTrigger(
|
||||
@@ -47,7 +47,7 @@ export async function updateTrigger(
|
||||
throw new Error(`Failed to update trigger: ${trigger.id} does not exist.`);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.trigger(trigger.id)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import {
|
||||
createUpdateSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction,
|
||||
@@ -10,7 +9,8 @@ import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
} from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { ClientOperationType, client } from "../CosmosClient";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
export async function updateUserDefinedFunction(
|
||||
@@ -53,7 +53,7 @@ export async function updateUserDefinedFunction(
|
||||
throw new Error(`Failed to update user defined function: ${userDefinedFunction.id} does not exist.`);
|
||||
}
|
||||
|
||||
const response = await client(ClientOperationType.WRITE)
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.userDefinedFunction(userDefinedFunction.id)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
export function getAuthorizationTokenUsingResourceTokens(
|
||||
resourceTokens: { [resourceId: string]: string },
|
||||
path: string,
|
||||
resourceId: string,
|
||||
): string {
|
||||
// console.log(`getting token for path: "${path}" and resourceId: "${resourceId}"`);
|
||||
|
||||
if (resourceTokens && Object.keys(resourceTokens).length > 0) {
|
||||
// For database account access(through getDatabaseAccount API), path and resourceId are "",
|
||||
// so in this case we return the first token to be used for creating the auth header as the
|
||||
// service will accept any token in this case
|
||||
if (!path && !resourceId) {
|
||||
return resourceTokens[Object.keys(resourceTokens)[0]];
|
||||
}
|
||||
|
||||
// If we have exact resource token for the path use it
|
||||
if (resourceId && resourceTokens[resourceId]) {
|
||||
return resourceTokens[resourceId];
|
||||
}
|
||||
|
||||
// minimum valid path /dbs
|
||||
if (!path || path.length < 4) {
|
||||
console.error(
|
||||
`Unable to get authotization token for Path:"${path}" and resourcerId:"${resourceId}". Invalid path.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
path = trimSlashFromLeftAndRight(path);
|
||||
const pathSegments = (path && path.split("/")) || [];
|
||||
|
||||
// Item path
|
||||
if (pathSegments.length === 6) {
|
||||
// Look for a container token matching the item path
|
||||
const containerPath = pathSegments.slice(0, 4).map(decodeURIComponent).join("/");
|
||||
if (resourceTokens[containerPath]) {
|
||||
return resourceTokens[containerPath];
|
||||
}
|
||||
}
|
||||
|
||||
// This is legacy behavior that lets someone use a resource token pointing ONLY at an ID
|
||||
// It was used when _rid was exposed by the SDK, but now that we are using user provided ids it is not needed
|
||||
// However removing it now would be a breaking change
|
||||
// if it's an incomplete path like /dbs/db1/colls/, start from the parent resource
|
||||
let index = pathSegments.length % 2 === 0 ? pathSegments.length - 1 : pathSegments.length - 2;
|
||||
for (; index > 0; index -= 2) {
|
||||
const id = decodeURI(pathSegments[index]);
|
||||
if (resourceTokens[id]) {
|
||||
return resourceTokens[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.error(`Unable to get authotization token for Path:"${path}" and resourcerId:"${resourceId}"`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimLeftSlashes = new RegExp("^[/]+");
|
||||
const trimRightSlashes = new RegExp("[/]+$");
|
||||
function trimSlashFromLeftAndRight(inputString: string): string {
|
||||
if (typeof inputString !== "string") {
|
||||
throw new Error("invalid input: input is not string");
|
||||
}
|
||||
|
||||
return inputString.replace(trimLeftSlashes, "").replace(trimRightSlashes, "");
|
||||
}
|
||||
@@ -1,25 +1,16 @@
|
||||
import {
|
||||
BackendApi,
|
||||
CassandraProxyEndpoints,
|
||||
JunoEndpoints,
|
||||
MongoProxyEndpoints,
|
||||
PortalBackendEndpoints,
|
||||
} from "Common/Constants";
|
||||
import {
|
||||
allowedAadEndpoints,
|
||||
allowedArcadiaEndpoints,
|
||||
allowedCassandraProxyEndpoints,
|
||||
allowedEmulatorEndpoints,
|
||||
allowedGraphEndpoints,
|
||||
allowedHostedExplorerEndpoints,
|
||||
allowedJunoOrigins,
|
||||
allowedMongoBackendEndpoints,
|
||||
allowedMongoProxyEndpoints,
|
||||
allowedMsalRedirectEndpoints,
|
||||
defaultAllowedArmEndpoints,
|
||||
defaultAllowedBackendEndpoints,
|
||||
validateEndpoint,
|
||||
} from "Utils/EndpointUtils";
|
||||
} from "Utils/EndpointValidation";
|
||||
|
||||
export enum Platform {
|
||||
Portal = "Portal",
|
||||
@@ -45,15 +36,7 @@ export interface ConfigContext {
|
||||
ARCADIA_ENDPOINT: string;
|
||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
||||
BACKEND_ENDPOINT?: string;
|
||||
PORTAL_BACKEND_ENDPOINT?: string;
|
||||
NEW_BACKEND_APIS?: BackendApi[];
|
||||
MONGO_BACKEND_ENDPOINT?: string;
|
||||
MONGO_PROXY_ENDPOINT?: string;
|
||||
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED?: boolean;
|
||||
NEW_MONGO_APIS?: string[];
|
||||
CASSANDRA_PROXY_ENDPOINT?: string;
|
||||
CASSANDRA_PROXY_OUTBOUND_IPS_ALLOWLISTED: boolean;
|
||||
NEW_CASSANDRA_APIS?: string[];
|
||||
PROXY_PATH?: string;
|
||||
JUNO_ENDPOINT: string;
|
||||
GITHUB_CLIENT_ID: string;
|
||||
@@ -81,8 +64,6 @@ let configContext: Readonly<ConfigContext> = {
|
||||
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
|
||||
`^https:\\/\\/.*\\.powerbi\\.com$`,
|
||||
`^https:\\/\\/.*\\.analysis-df\\.net$`,
|
||||
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
|
||||
`^https:\\/\\/.*\\.azure-test\\.net$`,
|
||||
], // Webpack injects this at build time
|
||||
gitSha: process.env.GIT_SHA,
|
||||
hostedExplorerURL: "https://cosmos.azure.com/",
|
||||
@@ -96,28 +77,8 @@ let configContext: Readonly<ConfigContext> = {
|
||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306
|
||||
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
|
||||
JUNO_ENDPOINT: JunoEndpoints.Prod,
|
||||
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Prod,
|
||||
NEW_BACKEND_APIS: [BackendApi.GenerateToken],
|
||||
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Prod,
|
||||
NEW_MONGO_APIS: [
|
||||
// "resourcelist",
|
||||
// "createDocument",
|
||||
// "readDocument",
|
||||
// "updateDocument",
|
||||
// "deleteDocument",
|
||||
// "createCollectionWithProxy",
|
||||
],
|
||||
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED: false,
|
||||
CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Prod,
|
||||
NEW_CASSANDRA_APIS: [
|
||||
// "postQuery",
|
||||
// "createOrDelete",
|
||||
// "getKeys",
|
||||
// "getSchema",
|
||||
],
|
||||
CASSANDRA_PROXY_OUTBOUND_IPS_ALLOWLISTED: false,
|
||||
isTerminalEnabled: false,
|
||||
isPhoenixEnabled: false,
|
||||
};
|
||||
@@ -163,18 +124,10 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
||||
delete newContext.BACKEND_ENDPOINT;
|
||||
}
|
||||
|
||||
if (!validateEndpoint(newContext.MONGO_PROXY_ENDPOINT, allowedMongoProxyEndpoints)) {
|
||||
delete newContext.MONGO_PROXY_ENDPOINT;
|
||||
}
|
||||
|
||||
if (!validateEndpoint(newContext.MONGO_BACKEND_ENDPOINT, allowedMongoBackendEndpoints)) {
|
||||
delete newContext.MONGO_BACKEND_ENDPOINT;
|
||||
}
|
||||
|
||||
if (!validateEndpoint(newContext.CASSANDRA_PROXY_ENDPOINT, allowedCassandraProxyEndpoints)) {
|
||||
delete newContext.CASSANDRA_PROXY_ENDPOINT;
|
||||
}
|
||||
|
||||
if (!validateEndpoint(newContext.JUNO_ENDPOINT, allowedJunoOrigins)) {
|
||||
delete newContext.JUNO_ENDPOINT;
|
||||
}
|
||||
@@ -192,7 +145,10 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
||||
|
||||
// Injected for local development. These will be removed in the production bundle by webpack
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
const port: string = process.env.PORT || "1234";
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://localhost:" + port,
|
||||
MONGO_BACKEND_ENDPOINT: "https://localhost:" + port,
|
||||
PROXY_PATH: "/proxy",
|
||||
EMULATOR_ENDPOINT: "https://localhost:8081",
|
||||
});
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export interface QueryRequestOptions {
|
||||
$skipToken?: string;
|
||||
$top?: number;
|
||||
subscriptions: string[];
|
||||
}
|
||||
|
||||
export interface QueryResponse {
|
||||
$skipToken: string;
|
||||
count: number;
|
||||
data: any;
|
||||
resultTruncated: boolean;
|
||||
totalRecords: number;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { MessageTypes } from "./MessageTypes";
|
||||
|
||||
// This is the current version of these messages
|
||||
export const DATA_EXPLORER_RPC_VERSION = "2";
|
||||
|
||||
// Data Explorer to Fabric
|
||||
|
||||
// TODO Remove when upgrading to Fabric v2
|
||||
export type DataExploreMessageV1 =
|
||||
| "ready"
|
||||
| {
|
||||
type: MessageTypes.GetAuthorizationToken;
|
||||
id: string;
|
||||
params: GetCosmosTokenMessageOptions[];
|
||||
}
|
||||
| {
|
||||
type: MessageTypes.GetAllResourceTokens;
|
||||
id: string;
|
||||
};
|
||||
// -----------------------------
|
||||
|
||||
export type DataExploreMessageV2 =
|
||||
| {
|
||||
type: MessageTypes.Ready;
|
||||
id: string;
|
||||
params: [string]; // version
|
||||
}
|
||||
| {
|
||||
type: MessageTypes.GetAuthorizationToken;
|
||||
id: string;
|
||||
params: GetCosmosTokenMessageOptions[];
|
||||
}
|
||||
| {
|
||||
type: MessageTypes.GetAllResourceTokens;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type GetCosmosTokenMessageOptions = {
|
||||
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";
|
||||
resourceType: "" | "dbs" | "colls" | "docs" | "sprocs" | "pkranges";
|
||||
resourceId: string;
|
||||
};
|
||||
@@ -88,13 +88,13 @@ export interface GenerateTokenResponse {
|
||||
}
|
||||
|
||||
export interface Subscription {
|
||||
uniqueDisplayName?: string;
|
||||
uniqueDisplayName: string;
|
||||
displayName: string;
|
||||
subscriptionId: string;
|
||||
tenantId?: string;
|
||||
tenantId: string;
|
||||
state: string;
|
||||
subscriptionPolicies?: SubscriptionPolicies;
|
||||
authorizationSource?: string;
|
||||
subscriptionPolicies: SubscriptionPolicies;
|
||||
authorizationSource: string;
|
||||
}
|
||||
|
||||
export interface SubscriptionPolicies {
|
||||
@@ -159,7 +159,6 @@ export interface Collection extends Resource {
|
||||
geospatialConfig?: GeospatialConfig;
|
||||
schema?: ISchema;
|
||||
requestSchema?: () => void;
|
||||
computedProperties?: ComputedProperties;
|
||||
}
|
||||
|
||||
export interface CollectionsWithPagination {
|
||||
@@ -198,13 +197,6 @@ export interface IndexingPolicy {
|
||||
spatialIndexes?: any;
|
||||
}
|
||||
|
||||
export interface ComputedProperty {
|
||||
name: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export type ComputedProperties = ComputedProperty[];
|
||||
|
||||
export interface PartitionKey {
|
||||
paths: string[];
|
||||
kind: "Hash" | "Range" | "MultiHash";
|
||||
@@ -465,11 +457,8 @@ export interface ContainerInfo {
|
||||
}
|
||||
|
||||
export interface IProvisionData {
|
||||
cosmosEndpoint?: string;
|
||||
cosmosEndpoint: string;
|
||||
poolId: string;
|
||||
databaseId?: string;
|
||||
containerId?: string;
|
||||
mode?: string;
|
||||
}
|
||||
|
||||
export interface IContainerData {
|
||||
@@ -612,14 +601,3 @@ export enum PhoenixErrorType {
|
||||
PhoenixFlightFallback = "PhoenixFlightFallback",
|
||||
UserMissingPermissionsError = "UserMissingPermissionsError",
|
||||
}
|
||||
|
||||
export interface CopilotEnabledConfiguration {
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface FeatureRegistration {
|
||||
name: string;
|
||||
properties: {
|
||||
state: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MessageTypes } from "Contracts/MessageTypes";
|
||||
import * as ActionContracts from "./ActionContracts";
|
||||
import * as Diagnostics from "./Diagnostics";
|
||||
import { MessageTypes } from "./MessageTypes";
|
||||
import * as Versions from "./Versions";
|
||||
|
||||
export { ActionContracts, Diagnostics, MessageTypes, Versions };
|
||||
|
||||
58
src/Contracts/FabricContract.ts
Normal file
58
src/Contracts/FabricContract.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { AuthorizationToken, MessageTypes } from "./MessageTypes";
|
||||
|
||||
export type FabricMessage =
|
||||
| {
|
||||
type: "newContainer";
|
||||
databaseName: string;
|
||||
}
|
||||
| {
|
||||
type: "initialize";
|
||||
message: {
|
||||
endpoint: string | undefined;
|
||||
error: string | undefined;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "openTab";
|
||||
databaseName: string;
|
||||
collectionName: string | undefined;
|
||||
}
|
||||
| {
|
||||
type: "authorizationToken";
|
||||
message: {
|
||||
id: string;
|
||||
error: string | undefined;
|
||||
data: AuthorizationToken | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
export type DataExploreMessage =
|
||||
| "ready"
|
||||
| {
|
||||
type: MessageTypes.TelemetryInfo;
|
||||
data: {
|
||||
action: "LoadDatabases";
|
||||
actionModifier: "success" | "start";
|
||||
defaultExperience: "SQL";
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: MessageTypes.GetAuthorizationToken;
|
||||
id: string;
|
||||
params: GetCosmosTokenMessageOptions[];
|
||||
};
|
||||
|
||||
export type GetCosmosTokenMessageOptions = {
|
||||
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";
|
||||
resourceType: "" | "dbs" | "colls" | "docs" | "sprocs" | "pkranges";
|
||||
resourceId: string;
|
||||
};
|
||||
|
||||
export type CosmosDBTokenResponse = {
|
||||
token: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export type CosmosDBConnectionInfoResponse = {
|
||||
endpoint: string;
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
import { AuthorizationToken } from "./MessageTypes";
|
||||
|
||||
// This is the version of these messages
|
||||
export const FABRIC_RPC_VERSION = "2";
|
||||
|
||||
// Fabric to Data Explorer
|
||||
|
||||
// TODO Deprecated. Remove this section once DE is updated
|
||||
export type FabricMessageV1 =
|
||||
| {
|
||||
type: "newContainer";
|
||||
databaseName: string;
|
||||
}
|
||||
| {
|
||||
type: "initialize";
|
||||
message: {
|
||||
endpoint: string | undefined;
|
||||
databaseId: string | undefined;
|
||||
resourceTokens: unknown | undefined;
|
||||
resourceTokensTimestamp: number | undefined;
|
||||
error: string | undefined;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "authorizationToken";
|
||||
message: {
|
||||
id: string;
|
||||
error: string | undefined;
|
||||
data: AuthorizationToken | undefined;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "allResourceTokens";
|
||||
message: {
|
||||
id: string;
|
||||
error: string | undefined;
|
||||
endpoint: string | undefined;
|
||||
databaseId: string | undefined;
|
||||
resourceTokens: unknown | undefined;
|
||||
resourceTokensTimestamp: number | undefined;
|
||||
};
|
||||
};
|
||||
// -----------------------------
|
||||
|
||||
export type FabricMessageV2 =
|
||||
| {
|
||||
type: "newContainer";
|
||||
databaseName: string;
|
||||
}
|
||||
| {
|
||||
type: "initialize";
|
||||
version: string;
|
||||
id: string;
|
||||
message: {
|
||||
connectionId: string;
|
||||
isVisible: boolean;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "authorizationToken";
|
||||
message: {
|
||||
id: string;
|
||||
error: string | undefined;
|
||||
data: AuthorizationToken | undefined;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "allResourceTokens_v2";
|
||||
message: {
|
||||
id: string;
|
||||
error: string | undefined;
|
||||
data: FabricDatabaseConnectionInfo | undefined;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "explorerVisible";
|
||||
message: {
|
||||
visible: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type CosmosDBTokenResponse = {
|
||||
token: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export type CosmosDBConnectionInfoResponse = {
|
||||
endpoint: string;
|
||||
databaseId: string;
|
||||
resourceTokens: { [resourceId: string]: string };
|
||||
};
|
||||
|
||||
export interface FabricDatabaseConnectionInfo extends CosmosDBConnectionInfoResponse {
|
||||
resourceTokensTimestamp: number;
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
/**
|
||||
* Messaging types used with Data Explorer <-> Portal communication,
|
||||
* Hosted <-> Explorer communication and Data Explorer -> Fabric communication.
|
||||
*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* WARNING: !!!!!!! YOU CAN ONLY ADD NEW TYPES TO THE END OF THIS ENUM !!!!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*
|
||||
* Enum are integers, so inserting or deleting a type will break the communication.
|
||||
*/
|
||||
export enum MessageTypes {
|
||||
TelemetryInfo,
|
||||
@@ -43,10 +37,9 @@ export enum MessageTypes {
|
||||
DisplayNPSSurvey,
|
||||
OpenVCoreMongoNetworkingBlade,
|
||||
OpenVCoreMongoConnectionStringsBlade,
|
||||
GetAuthorizationToken, // Data Explorer -> Fabric
|
||||
GetAllResourceTokens, // Data Explorer -> Fabric
|
||||
Ready, // Data Explorer -> Fabric
|
||||
OpenCESCVAFeedbackBlade,
|
||||
|
||||
// Data Explorer -> Fabric communication
|
||||
GetAuthorizationToken,
|
||||
}
|
||||
|
||||
export interface AuthorizationToken {
|
||||
|
||||
@@ -135,7 +135,6 @@ export interface Collection extends CollectionBase {
|
||||
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||
geospatialConfig: ko.Observable<DataModels.GeospatialConfig>;
|
||||
documentIds: ko.ObservableArray<DocumentId>;
|
||||
computedProperties: ko.Observable<DataModels.ComputedProperties>;
|
||||
|
||||
cassandraKeys: CassandraTableKeys;
|
||||
cassandraSchema: CassandraTableKey[];
|
||||
@@ -387,10 +386,9 @@ export interface DataExplorerInputsFrame {
|
||||
dnsSuffix?: string;
|
||||
serverId?: string;
|
||||
extensionEndpoint?: string;
|
||||
mongoProxyEndpoint?: string;
|
||||
cassandraProxyEndpoint?: string;
|
||||
subscriptionType?: SubscriptionType;
|
||||
quotaId?: string;
|
||||
addCollectionDefaultFlight?: string;
|
||||
isTryCosmosDBSubscription?: boolean;
|
||||
loadDatabaseAccountTimestamp?: number;
|
||||
sharedThroughputMinimum?: number;
|
||||
@@ -408,7 +406,6 @@ export interface DataExplorerInputsFrame {
|
||||
features?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
feedbackPolicies?: any;
|
||||
}
|
||||
|
||||
export interface SelfServeFrameInputs {
|
||||
|
||||
@@ -109,7 +109,6 @@ describe("iframe rendering when there is no data", () => {
|
||||
theme: 4,
|
||||
},
|
||||
},
|
||||
origin: "http://localhost",
|
||||
};
|
||||
|
||||
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
||||
@@ -130,7 +129,6 @@ describe("iframe rendering when there is no data", () => {
|
||||
theme: 2,
|
||||
},
|
||||
},
|
||||
origin: "http://localhost",
|
||||
};
|
||||
|
||||
const divElement = `<div id="${Heatmap.elementId}"></div>`;
|
||||
|
||||
1954
src/Definitions/datatables.d.ts
vendored
Normal file
1954
src/Definitions/datatables.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
src/Definitions/jquery-typescript.d.ts
vendored
2
src/Definitions/jquery-typescript.d.ts
vendored
@@ -5,7 +5,7 @@
|
||||
* https://github.com/running-coder/jquery-typeahead/issues/156
|
||||
* TODO: Replace this minimum definition by the official one when it comes out.
|
||||
*/
|
||||
/// <reference types="jquery" />
|
||||
/// <reference path="jquery.d.ts" />
|
||||
|
||||
interface JQueryTypeaheadParam {
|
||||
input: string;
|
||||
|
||||
2
src/Definitions/jquery-ui.d.ts
vendored
2
src/Definitions/jquery-ui.d.ts
vendored
@@ -3,7 +3,7 @@
|
||||
// Definitions by: Boris Yankov <https://github.com/borisyankov/>, John Reilly <https://github.com/johnnyreilly>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
/// <reference types="jquery"/>
|
||||
/// <reference path="jquery.d.ts"/>
|
||||
|
||||
declare namespace JQueryUI {
|
||||
// Accordion //////////////////////////////////////////////////
|
||||
|
||||
1890
src/Definitions/jquery.d.ts
vendored
Normal file
1890
src/Definitions/jquery.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4
src/Definitions/less.d.ts
vendored
4
src/Definitions/less.d.ts
vendored
@@ -1,4 +0,0 @@
|
||||
declare module "*.less" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
@@ -129,22 +129,20 @@ export const createCollectionContextMenuButton = (
|
||||
});
|
||||
}
|
||||
|
||||
if (configContext.platform !== Platform.Fabric) {
|
||||
items.push({
|
||||
iconSrc: DeleteCollectionIcon,
|
||||
onClick: () => {
|
||||
useSelectedNode.getState().setSelectedNode(selectedCollection);
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getCollectionName(),
|
||||
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
},
|
||||
label: `Delete ${getCollectionName()}`,
|
||||
styleClass: "deleteCollectionMenuItem",
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
iconSrc: DeleteCollectionIcon,
|
||||
onClick: () => {
|
||||
useSelectedNode.getState().setSelectedNode(selectedCollection);
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getCollectionName(),
|
||||
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
},
|
||||
label: `Delete ${getCollectionName()}`,
|
||||
styleClass: "deleteCollectionMenuItem",
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@ export interface AccordionItemComponentProps {
|
||||
isExpanded?: boolean;
|
||||
containerStyles?: React.CSSProperties;
|
||||
styles?: React.CSSProperties;
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
interface AccordionItemComponentState {
|
||||
|
||||
@@ -16,7 +16,6 @@ export interface CollapsiblePanelProps {
|
||||
isCollapsed: boolean;
|
||||
onCollapsedChanged: (newValue: boolean) => void;
|
||||
collapseToLeft?: boolean;
|
||||
children: JSX.Element | JSX.Element[];
|
||||
}
|
||||
|
||||
export class CollapsiblePanel extends React.Component<CollapsiblePanelProps> {
|
||||
|
||||
@@ -7,7 +7,6 @@ describe("CollapsibleSectionComponent", () => {
|
||||
const props: CollapsibleSectionProps = {
|
||||
title: "Sample title",
|
||||
isExpandedByDefault: true,
|
||||
children: <></>,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<CollapsibleSectionComponent {...props} />);
|
||||
|
||||
@@ -7,7 +7,6 @@ export interface CollapsibleSectionProps {
|
||||
title: string;
|
||||
isExpandedByDefault: boolean;
|
||||
onExpand?: () => void;
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
export interface CollapsibleSectionState {
|
||||
|
||||
@@ -99,7 +99,7 @@ export class DiffEditorViewModel {
|
||||
) {
|
||||
this.editorContainer = document.getElementById(this.getEditorId());
|
||||
this.editorContainer.innerHTML = "";
|
||||
const options: monaco.editor.IStandaloneDiffEditorConstructionOptions = {
|
||||
const options: monaco.editor.IDiffEditorConstructionOptions = {
|
||||
lineNumbers: this.params.lineNumbers || "off",
|
||||
fontSize: 12,
|
||||
ariaLabel: this.params.ariaLabel,
|
||||
|
||||
@@ -52,11 +52,7 @@ class EditorViewModel extends JsonEditorViewModel {
|
||||
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
|
||||
const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service");
|
||||
const monaco = await loadMonaco();
|
||||
monaco.languages.registerCompletionItemProvider(
|
||||
"sql",
|
||||
// TODO cosmos-language-service could be outdated
|
||||
new SqlCompletionItemProvider() as unknown as monaco.languages.CompletionItemProvider,
|
||||
);
|
||||
monaco.languages.registerCompletionItemProvider("sql", new SqlCompletionItemProvider());
|
||||
EditorViewModel.providerRegistered.push("sql");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
|
||||
public componentDidUpdate(previous: EditorReactProps) {
|
||||
if (this.props.content !== previous.content) {
|
||||
this.editor?.setValue(this.props.content);
|
||||
this.editor.setValue(this.props.content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
* Create the monaco editor and attach to DOM
|
||||
*/
|
||||
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
const options: monaco.editor.IEditorConstructionOptions = {
|
||||
language: this.props.language,
|
||||
value: this.props.content,
|
||||
readOnly: this.props.isReadOnly,
|
||||
@@ -111,7 +111,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
|
||||
|
||||
this.rootNode.innerHTML = "";
|
||||
const monaco = await loadMonaco();
|
||||
createCallback(monaco?.editor?.create(this.rootNode, options));
|
||||
createCallback(monaco.editor.create(this.rootNode, options));
|
||||
|
||||
if (this.rootNode.innerHTML) {
|
||||
this.setState({
|
||||
|
||||
@@ -90,7 +90,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
|
||||
protected async createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
|
||||
this.registerCompletionItemProvider();
|
||||
this.editorContainer = document.getElementById(this.getEditorId());
|
||||
const options: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
const options: monaco.editor.IEditorConstructionOptions = {
|
||||
value: content,
|
||||
language: this.getEditorLanguage(),
|
||||
readOnly: this.params.isReadOnly,
|
||||
|
||||
@@ -29,6 +29,6 @@ describe("CodeOfConduct", () => {
|
||||
const wrapper = shallow(<CodeOfConduct {...codeOfConductProps} />);
|
||||
wrapper.find(".genericPaneSubmitBtn").first().simulate("click");
|
||||
await Promise.resolve();
|
||||
expect(codeOfConductProps.onAcceptCodeOfConduct).toHaveBeenCalled();
|
||||
expect(codeOfConductProps.onAcceptCodeOfConduct).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import { updateUserContext } from "../../../UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import { CollectionSettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
||||
import { SettingsComponent, SettingsComponentProps, SettingsComponentState } from "./SettingsComponent";
|
||||
import { TtlType, isDirty } from "./SettingsUtils";
|
||||
import { isDirty, TtlType } from "./SettingsUtils";
|
||||
import { collection } from "./TestUtils";
|
||||
jest.mock("../../../Common/dataAccess/getIndexTransformationProgress", () => ({
|
||||
getIndexTransformationProgress: jest.fn().mockReturnValue(undefined),
|
||||
@@ -190,8 +190,8 @@ describe("SettingsComponent", () => {
|
||||
id: "id",
|
||||
};
|
||||
await settingsComponentInstance.onSaveClick();
|
||||
expect(updateCollection).toHaveBeenCalled();
|
||||
expect(updateOffer).toHaveBeenCalled();
|
||||
expect(updateCollection).toBeCalled();
|
||||
expect(updateOffer).toBeCalled();
|
||||
});
|
||||
|
||||
it("revert resets state values", async () => {
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "@fluentui/react";
|
||||
import {
|
||||
ComputedPropertiesComponent,
|
||||
ComputedPropertiesComponentProps,
|
||||
} from "Explorer/Controls/Settings/SettingsSubComponents/ComputedPropertiesComponent";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
||||
import * as React from "react";
|
||||
import DiscardIcon from "../../../../images/discard.svg";
|
||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||
@@ -23,10 +18,6 @@ import { userContext } from "../../../UserContext";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import {
|
||||
PartitionKeyComponent,
|
||||
PartitionKeyComponentProps,
|
||||
} from "../../Controls/Settings/SettingsSubComponents/PartitionKeyComponent";
|
||||
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
||||
import "./SettingsComponent.less";
|
||||
@@ -112,11 +103,6 @@ export interface SettingsComponentState {
|
||||
indexesToAdd: AddMongoIndexProps[];
|
||||
indexTransformationProgress: number;
|
||||
|
||||
computedPropertiesContent: DataModels.ComputedProperties;
|
||||
computedPropertiesContentBaseline: DataModels.ComputedProperties;
|
||||
shouldDiscardComputedProperties: boolean;
|
||||
isComputedPropertiesDirty: boolean;
|
||||
|
||||
conflictResolutionPolicyMode: DataModels.ConflictResolutionMode;
|
||||
conflictResolutionPolicyModeBaseline: DataModels.ConflictResolutionMode;
|
||||
conflictResolutionPolicyPath: string;
|
||||
@@ -141,9 +127,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private offer: DataModels.Offer;
|
||||
private changeFeedPolicyVisible: boolean;
|
||||
private isFixedContainer: boolean;
|
||||
private shouldShowComputedPropertiesEditor: boolean;
|
||||
private shouldShowIndexingPolicyEditor: boolean;
|
||||
private shouldShowPartitionKeyEditor: boolean;
|
||||
private totalThroughputUsed: number;
|
||||
public mongoDBCollectionResource: MongoDBCollectionResource;
|
||||
|
||||
@@ -155,9 +139,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.collection = this.props.settingsTab.collection as ViewModels.Collection;
|
||||
this.offer = this.collection?.offer();
|
||||
this.isAnalyticalStorageEnabled = !!this.collection?.analyticalStorageTtl();
|
||||
this.shouldShowComputedPropertiesEditor = userContext.apiType === "SQL";
|
||||
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
|
||||
this.shouldShowPartitionKeyEditor = userContext.apiType === "SQL" && isRunningOnPublicCloud();
|
||||
|
||||
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
|
||||
|
||||
@@ -209,11 +191,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
isMongoIndexingPolicyDiscardable: false,
|
||||
indexTransformationProgress: undefined,
|
||||
|
||||
computedPropertiesContent: undefined,
|
||||
computedPropertiesContentBaseline: undefined,
|
||||
shouldDiscardComputedProperties: false,
|
||||
isComputedPropertiesDirty: false,
|
||||
|
||||
conflictResolutionPolicyMode: undefined,
|
||||
conflictResolutionPolicyModeBaseline: undefined,
|
||||
conflictResolutionPolicyPath: undefined,
|
||||
@@ -304,7 +281,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.state.isSubSettingsSaveable ||
|
||||
this.state.isIndexingPolicyDirty ||
|
||||
this.state.isConflictResolutionDirty ||
|
||||
this.state.isComputedPropertiesDirty ||
|
||||
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicySaveable)
|
||||
);
|
||||
};
|
||||
@@ -315,7 +291,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.state.isSubSettingsDiscardable ||
|
||||
this.state.isIndexingPolicyDirty ||
|
||||
this.state.isConflictResolutionDirty ||
|
||||
this.state.isComputedPropertiesDirty ||
|
||||
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicyDiscardable)
|
||||
);
|
||||
};
|
||||
@@ -420,9 +395,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
isMongoIndexingPolicySaveable: false,
|
||||
isMongoIndexingPolicyDiscardable: false,
|
||||
isConflictResolutionDirty: false,
|
||||
computedPropertiesContent: this.state.computedPropertiesContentBaseline,
|
||||
shouldDiscardComputedProperties: true,
|
||||
isComputedPropertiesDirty: false,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -542,31 +514,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private onMongoIndexingPolicyDiscardableChange = (isMongoIndexingPolicyDiscardable: boolean): void =>
|
||||
this.setState({ isMongoIndexingPolicyDiscardable });
|
||||
|
||||
private onComputedPropertiesContentChange = (newComputedProperties: DataModels.ComputedProperties): void =>
|
||||
this.setState({ computedPropertiesContent: newComputedProperties });
|
||||
|
||||
private resetShouldDiscardComputedProperties = (): void => this.setState({ shouldDiscardComputedProperties: false });
|
||||
|
||||
private logComputedPropertiesSuccessMessage = (): void => {
|
||||
if (this.props.settingsTab.onLoadStartKey) {
|
||||
traceSuccess(
|
||||
Action.Tab,
|
||||
{
|
||||
databaseName: this.collection.databaseId,
|
||||
collectionName: this.collection.id(),
|
||||
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.props.settingsTab.tabTitle(),
|
||||
},
|
||||
this.props.settingsTab.onLoadStartKey,
|
||||
);
|
||||
this.props.settingsTab.onLoadStartKey = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private onComputedPropertiesDirtyChange = (isComputedPropertiesDirty: boolean): void =>
|
||||
this.setState({ isComputedPropertiesDirty: isComputedPropertiesDirty });
|
||||
|
||||
private calculateTotalThroughputUsed = (): void => {
|
||||
this.totalThroughputUsed = 0;
|
||||
(useDatabases.getState().databases || []).forEach(async (database) => {
|
||||
@@ -689,6 +636,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const indexingPolicyContent = this.collection.indexingPolicy();
|
||||
const conflictResolutionPolicy: DataModels.ConflictResolutionPolicy =
|
||||
this.collection.conflictResolutionPolicy && this.collection.conflictResolutionPolicy();
|
||||
|
||||
const conflictResolutionPolicyMode = parseConflictResolutionMode(conflictResolutionPolicy?.mode);
|
||||
const conflictResolutionPolicyPath = conflictResolutionPolicy?.conflictResolutionPath;
|
||||
const conflictResolutionPolicyProcedure = parseConflictResolutionProcedure(
|
||||
@@ -697,12 +645,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const geospatialConfigTypeString: string =
|
||||
(this.collection.geospatialConfig && this.collection.geospatialConfig()?.type) || GeospatialConfigType.Geometry;
|
||||
const geoSpatialConfigType = GeospatialConfigType[geospatialConfigTypeString as keyof typeof GeospatialConfigType];
|
||||
let computedPropertiesContent = this.collection.computedProperties();
|
||||
if (!computedPropertiesContent || computedPropertiesContent.length === 0) {
|
||||
computedPropertiesContent = [
|
||||
{ name: "name_of_property", query: "query_to_compute_property" },
|
||||
] as DataModels.ComputedProperties;
|
||||
}
|
||||
|
||||
return {
|
||||
throughput: offerThroughput,
|
||||
@@ -729,8 +671,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
conflictResolutionPolicyProcedureBaseline: conflictResolutionPolicyProcedure,
|
||||
geospatialConfigType: geoSpatialConfigType,
|
||||
geospatialConfigTypeBaseline: geoSpatialConfigType,
|
||||
computedPropertiesContent: computedPropertiesContent,
|
||||
computedPropertiesContentBaseline: computedPropertiesContent,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -847,12 +787,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private saveCollectionSettings = async (startKey: number): Promise<void> => {
|
||||
const newCollection: DataModels.Collection = { ...this.collection.rawDataModel };
|
||||
|
||||
if (
|
||||
this.state.isSubSettingsSaveable ||
|
||||
this.state.isIndexingPolicyDirty ||
|
||||
this.state.isConflictResolutionDirty ||
|
||||
this.state.isComputedPropertiesDirty
|
||||
) {
|
||||
if (this.state.isSubSettingsSaveable || this.state.isIndexingPolicyDirty || this.state.isConflictResolutionDirty) {
|
||||
let defaultTtl: number;
|
||||
switch (this.state.timeToLive) {
|
||||
case TtlType.On:
|
||||
@@ -890,10 +825,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
newCollection.conflictResolutionPolicy = conflictResolutionChanges;
|
||||
}
|
||||
|
||||
if (this.state.isComputedPropertiesDirty) {
|
||||
newCollection.computedProperties = this.state.computedPropertiesContent;
|
||||
}
|
||||
|
||||
const updatedCollection: DataModels.Collection = await updateCollection(
|
||||
this.collection.databaseId,
|
||||
this.collection.id(),
|
||||
@@ -907,7 +838,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.collection.conflictResolutionPolicy(updatedCollection.conflictResolutionPolicy);
|
||||
this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy);
|
||||
this.collection.geospatialConfig(updatedCollection.geospatialConfig);
|
||||
this.collection.computedProperties(updatedCollection.computedProperties);
|
||||
|
||||
if (wasIndexingPolicyModified) {
|
||||
await this.refreshIndexTransformationProgress();
|
||||
@@ -918,7 +848,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
isSubSettingsDiscardable: false,
|
||||
isIndexingPolicyDirty: false,
|
||||
isConflictResolutionDirty: false,
|
||||
isComputedPropertiesDirty: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1113,16 +1042,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
onMongoIndexingPolicyDiscardableChange: this.onMongoIndexingPolicyDiscardableChange,
|
||||
};
|
||||
|
||||
const computedPropertiesComponentProps: ComputedPropertiesComponentProps = {
|
||||
computedPropertiesContent: this.state.computedPropertiesContent,
|
||||
computedPropertiesContentBaseline: this.state.computedPropertiesContentBaseline,
|
||||
logComputedPropertiesSuccessMessage: this.logComputedPropertiesSuccessMessage,
|
||||
onComputedPropertiesContentChange: this.onComputedPropertiesContentChange,
|
||||
onComputedPropertiesDirtyChange: this.onComputedPropertiesDirtyChange,
|
||||
resetShouldDiscardComputedProperties: this.resetShouldDiscardComputedProperties,
|
||||
shouldDiscardComputedProperties: this.state.shouldDiscardComputedProperties,
|
||||
};
|
||||
|
||||
const conflictResolutionPolicyComponentProps: ConflictResolutionComponentProps = {
|
||||
collection: this.collection,
|
||||
conflictResolutionPolicyMode: this.state.conflictResolutionPolicyMode,
|
||||
@@ -1137,12 +1056,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
onConflictResolutionDirtyChange: this.onConflictResolutionDirtyChange,
|
||||
};
|
||||
|
||||
const partitionKeyComponentProps: PartitionKeyComponentProps = {
|
||||
database: useDatabases.getState().findDatabaseWithId(this.collection.databaseId),
|
||||
collection: this.collection,
|
||||
explorer: this.props.settingsTab.getContainer(),
|
||||
};
|
||||
|
||||
const tabs: SettingsV2TabInfo[] = [];
|
||||
if (!hasDatabaseSharedThroughput(this.collection) && this.offer) {
|
||||
tabs.push({
|
||||
@@ -1178,20 +1091,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
});
|
||||
}
|
||||
|
||||
if (this.shouldShowPartitionKeyEditor) {
|
||||
tabs.push({
|
||||
tab: SettingsV2TabTypes.PartitionKeyTab,
|
||||
content: <PartitionKeyComponent {...partitionKeyComponentProps} />,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.shouldShowComputedPropertiesEditor) {
|
||||
tabs.push({
|
||||
tab: SettingsV2TabTypes.ComputedPropertiesTab,
|
||||
content: <ComputedPropertiesComponent {...computedPropertiesComponentProps} />,
|
||||
});
|
||||
}
|
||||
|
||||
const pivotProps: IPivotProps = {
|
||||
onLinkClick: this.onPivotChange,
|
||||
selectedKey: SettingsV2TabTypes[this.state.selectedTab],
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
getThroughputApplyLongDelayMessage,
|
||||
getThroughputApplyShortDelayMessage,
|
||||
getToolTipContainer,
|
||||
indexingPolicynUnsavedWarningMessage,
|
||||
manualToAutoscaleDisclaimerElement,
|
||||
mongoIndexTransformationRefreshingMessage,
|
||||
mongoIndexingPolicyAADError,
|
||||
@@ -38,6 +39,7 @@ class SettingsRenderUtilsTestComponent extends React.Component {
|
||||
|
||||
{manualToAutoscaleDisclaimerElement}
|
||||
{ttlWarning}
|
||||
{indexingPolicynUnsavedWarningMessage}
|
||||
{updateThroughputDelayedApplyWarningMessage}
|
||||
|
||||
{getThroughputApplyDelayedMessage(false, 1000, "RU/s", "sampleDb", "sampleCollection", 2000)}
|
||||
|
||||
@@ -61,8 +61,6 @@ export interface PriceBreakdown {
|
||||
currencySign: string;
|
||||
}
|
||||
|
||||
export type editorType = "indexPolicy" | "computedProperties";
|
||||
|
||||
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "windowtext" } };
|
||||
|
||||
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
||||
@@ -256,10 +254,9 @@ export const ttlWarning: JSX.Element = (
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const unsavedEditorWarningMessage = (editor: editorType): JSX.Element => (
|
||||
export const indexingPolicynUnsavedWarningMessage: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
You have not saved the latest changes made to your{" "}
|
||||
{editor === "indexPolicy" ? "indexing policy" : "computed properties"}. Please click save to confirm the changes.
|
||||
You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.
|
||||
</Text>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { ComputedPropertiesComponent, ComputedPropertiesComponentProps } from "./ComputedPropertiesComponent";
|
||||
|
||||
describe("ComputedPropertiesComponent", () => {
|
||||
const initialComputedPropertiesContent: DataModels.ComputedProperties = [
|
||||
{
|
||||
name: "prop1",
|
||||
query: "query1",
|
||||
},
|
||||
];
|
||||
const baseProps: ComputedPropertiesComponentProps = {
|
||||
computedPropertiesContent: initialComputedPropertiesContent,
|
||||
computedPropertiesContentBaseline: initialComputedPropertiesContent,
|
||||
logComputedPropertiesSuccessMessage: () => {
|
||||
return;
|
||||
},
|
||||
onComputedPropertiesContentChange: () => {
|
||||
return;
|
||||
},
|
||||
onComputedPropertiesDirtyChange: () => {
|
||||
return;
|
||||
},
|
||||
resetShouldDiscardComputedProperties: () => {
|
||||
return;
|
||||
},
|
||||
shouldDiscardComputedProperties: false,
|
||||
};
|
||||
|
||||
it("renders", () => {
|
||||
const wrapper = shallow(<ComputedPropertiesComponent {...baseProps} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("computed properties are reset", () => {
|
||||
const wrapper = shallow(<ComputedPropertiesComponent {...baseProps} />);
|
||||
|
||||
const computedPropertiesComponentInstance = wrapper.instance() as ComputedPropertiesComponent;
|
||||
const resetComputedPropertiesEditorMockFn = jest.fn();
|
||||
computedPropertiesComponentInstance.resetComputedPropertiesEditor = resetComputedPropertiesEditorMockFn;
|
||||
|
||||
wrapper.setProps({ shouldDiscardComputedProperties: true });
|
||||
wrapper.update();
|
||||
expect(resetComputedPropertiesEditorMockFn.mock.calls.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("dirty is set", () => {
|
||||
let computedPropertiesComponent = new ComputedPropertiesComponent(baseProps);
|
||||
expect(computedPropertiesComponent.IsComponentDirty()).toEqual(false);
|
||||
|
||||
const newProps = { ...baseProps, computedPropertiesContent: undefined as DataModels.ComputedProperties };
|
||||
computedPropertiesComponent = new ComputedPropertiesComponent(newProps);
|
||||
expect(computedPropertiesComponent.IsComponentDirty()).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -1,128 +0,0 @@
|
||||
import { FontIcon, Link, MessageBar, MessageBarType, Stack, Text } from "@fluentui/react";
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "Explorer/Controls/Settings/SettingsRenderUtils";
|
||||
import { isDirty } from "Explorer/Controls/Settings/SettingsUtils";
|
||||
import { loadMonaco } from "Explorer/LazyMonaco";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
|
||||
export interface ComputedPropertiesComponentProps {
|
||||
computedPropertiesContent: DataModels.ComputedProperties;
|
||||
computedPropertiesContentBaseline: DataModels.ComputedProperties;
|
||||
logComputedPropertiesSuccessMessage: () => void;
|
||||
onComputedPropertiesContentChange: (newComputedProperties: DataModels.ComputedProperties) => void;
|
||||
onComputedPropertiesDirtyChange: (isComputedPropertiesDirty: boolean) => void;
|
||||
resetShouldDiscardComputedProperties: () => void;
|
||||
shouldDiscardComputedProperties: boolean;
|
||||
}
|
||||
|
||||
interface ComputedPropertiesComponentState {
|
||||
computedPropertiesContentIsValid: boolean;
|
||||
}
|
||||
|
||||
export class ComputedPropertiesComponent extends React.Component<
|
||||
ComputedPropertiesComponentProps,
|
||||
ComputedPropertiesComponentState
|
||||
> {
|
||||
private shouldCheckComponentIsDirty = true;
|
||||
private computedPropertiesDiv = React.createRef<HTMLDivElement>();
|
||||
private computedPropertiesEditor: monaco.editor.IStandaloneCodeEditor;
|
||||
|
||||
constructor(props: ComputedPropertiesComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
computedPropertiesContentIsValid: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
if (this.props.shouldDiscardComputedProperties) {
|
||||
this.resetComputedPropertiesEditor();
|
||||
this.props.resetShouldDiscardComputedProperties();
|
||||
}
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.resetComputedPropertiesEditor();
|
||||
this.onComponentUpdate();
|
||||
}
|
||||
|
||||
public resetComputedPropertiesEditor = (): void => {
|
||||
if (!this.computedPropertiesEditor) {
|
||||
this.createComputedPropertiesEditor();
|
||||
} else {
|
||||
const indexingPolicyEditorModel = this.computedPropertiesEditor.getModel();
|
||||
const value: string = JSON.stringify(this.props.computedPropertiesContent, undefined, 4);
|
||||
indexingPolicyEditorModel.setValue(value);
|
||||
}
|
||||
this.onComponentUpdate();
|
||||
};
|
||||
|
||||
private onComponentUpdate = (): void => {
|
||||
if (!this.shouldCheckComponentIsDirty) {
|
||||
this.shouldCheckComponentIsDirty = true;
|
||||
return;
|
||||
}
|
||||
this.props.onComputedPropertiesDirtyChange(this.IsComponentDirty());
|
||||
this.shouldCheckComponentIsDirty = false;
|
||||
};
|
||||
|
||||
public IsComponentDirty = (): boolean => {
|
||||
if (
|
||||
isDirty(this.props.computedPropertiesContent, this.props.computedPropertiesContentBaseline) &&
|
||||
this.state.computedPropertiesContentIsValid
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
private async createComputedPropertiesEditor(): Promise<void> {
|
||||
const value: string = JSON.stringify(this.props.computedPropertiesContent, undefined, 4);
|
||||
const monaco = await loadMonaco();
|
||||
this.computedPropertiesEditor = monaco.editor.create(this.computedPropertiesDiv.current, {
|
||||
value: value,
|
||||
language: "json",
|
||||
ariaLabel: "Computed properties",
|
||||
});
|
||||
if (this.computedPropertiesEditor) {
|
||||
const computedPropertiesEditorModel = this.computedPropertiesEditor.getModel();
|
||||
computedPropertiesEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this));
|
||||
this.props.logComputedPropertiesSuccessMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private onEditorContentChange = (): void => {
|
||||
const computedPropertiesEditorModel = this.computedPropertiesEditor.getModel();
|
||||
try {
|
||||
const newComputedPropertiesContent = JSON.parse(
|
||||
computedPropertiesEditorModel.getValue(),
|
||||
) as DataModels.ComputedProperties;
|
||||
this.props.onComputedPropertiesContentChange(newComputedPropertiesContent);
|
||||
this.setState({ computedPropertiesContentIsValid: true });
|
||||
} catch (e) {
|
||||
this.setState({ computedPropertiesContentIsValid: false });
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
{isDirty(this.props.computedPropertiesContent, this.props.computedPropertiesContentBaseline) && (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>
|
||||
{unsavedEditorWarningMessage("computedProperties")}
|
||||
</MessageBar>
|
||||
)}
|
||||
<Text style={{ marginLeft: "30px", marginBottom: "10px" }}>
|
||||
<Link target="_blank" href="https://aka.ms/computed-properties-preview/">
|
||||
{"Learn more"} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
  about how to define computed properties and how to use them.
|
||||
</Text>
|
||||
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.computedPropertiesDiv}></div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { loadMonaco } from "../../../LazyMonaco";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
|
||||
import { indexingPolicynUnsavedWarningMessage, titleAndInputStackProps } from "../SettingsRenderUtils";
|
||||
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||
import { IndexingPolicyRefreshComponent } from "./IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||
|
||||
@@ -120,7 +120,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
refreshIndexTransformationProgress={this.props.refreshIndexTransformationProgress}
|
||||
/>
|
||||
{isDirty(this.props.indexingPolicyContent, this.props.indexingPolicyContentBaseline) && (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{unsavedEditorWarningMessage("indexPolicy")}</MessageBar>
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{indexingPolicynUnsavedWarningMessage}</MessageBar>
|
||||
)}
|
||||
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.indexingPolicyDiv}></div>
|
||||
</Stack>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils";
|
||||
import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent";
|
||||
import { renderToString } from "react-dom/server";
|
||||
|
||||
describe("MongoIndexingPolicyComponent", () => {
|
||||
const baseProps: MongoIndexingPolicyComponentProps = {
|
||||
@@ -84,7 +84,7 @@ describe("MongoIndexingPolicyComponent", () => {
|
||||
];
|
||||
|
||||
test.each(cases)(
|
||||
"mongo indexing policy saveable and discardable",
|
||||
"",
|
||||
(
|
||||
notification: MongoNotificationMessage,
|
||||
indexToDropIsPresent: boolean,
|
||||
@@ -111,10 +111,8 @@ describe("MongoIndexingPolicyComponent", () => {
|
||||
);
|
||||
if (mongoWarningNotificationMessage) {
|
||||
const elementAsString = renderToString(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage());
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(elementAsString).toContain(mongoWarningNotificationMessage);
|
||||
} else {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toBeUndefined();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
addMongoIndexStackProps,
|
||||
createAndAddMongoIndexStackProps,
|
||||
customDetailsListStyles,
|
||||
indexingPolicynUnsavedWarningMessage,
|
||||
infoAndToolTipTextStyle,
|
||||
mediumWidthStackStyles,
|
||||
mongoCompoundIndexNotSupportedMessage,
|
||||
@@ -26,16 +27,15 @@ import {
|
||||
onRenderRow,
|
||||
separatorStyles,
|
||||
subComponentStackProps,
|
||||
unsavedEditorWarningMessage,
|
||||
} from "../../SettingsRenderUtils";
|
||||
import {
|
||||
AddMongoIndexProps,
|
||||
MongoIndexIdField,
|
||||
MongoIndexTypes,
|
||||
MongoNotificationType,
|
||||
getMongoIndexType,
|
||||
getMongoIndexTypeText,
|
||||
isIndexTransforming,
|
||||
MongoIndexIdField,
|
||||
MongoIndexTypes,
|
||||
MongoNotificationType,
|
||||
} from "../../SettingsUtils";
|
||||
import { IndexingPolicyRefreshComponent } from "../IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||
import { AddMongoIndexComponent } from "./AddMongoIndexComponent";
|
||||
@@ -297,7 +297,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
if (this.getMongoWarningNotificationMessage()) {
|
||||
warningMessage = this.getMongoWarningNotificationMessage();
|
||||
} else if (this.isMongoIndexingPolicySaveable()) {
|
||||
warningMessage = unsavedEditorWarningMessage("indexPolicy");
|
||||
warningMessage = indexingPolicynUnsavedWarningMessage;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
import {
|
||||
DefaultButton,
|
||||
FontWeights,
|
||||
Link,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
PrimaryButton,
|
||||
ProgressIndicator,
|
||||
Stack,
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
|
||||
import { handleError } from "Common/ErrorHandlingUtils";
|
||||
import { cancelDataTransferJob, pollDataTransferJob } from "Common/dataAccess/dataTransfers";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { ChangePartitionKeyPane } from "Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane";
|
||||
import {
|
||||
CosmosSqlDataTransferDataSourceSink,
|
||||
DataTransferJobGetResults,
|
||||
} from "Utils/arm/generatedClients/dataTransferService/types";
|
||||
import { refreshDataTransferJobs, useDataTransferJobs } from "hooks/useDataTransferJobs";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
|
||||
export interface PartitionKeyComponentProps {
|
||||
database: ViewModels.Database;
|
||||
collection: ViewModels.Collection;
|
||||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({ database, collection, explorer }) => {
|
||||
const { dataTransferJobs } = useDataTransferJobs();
|
||||
const [portalDataTransferJob, setPortalDataTransferJob] = React.useState<DataTransferJobGetResults>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const loadDataTransferJobs = refreshDataTransferOperations;
|
||||
loadDataTransferJobs();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const currentJob = findPortalDataTransferJob();
|
||||
setPortalDataTransferJob(currentJob);
|
||||
startPollingforUpdate(currentJob);
|
||||
}, [dataTransferJobs]);
|
||||
|
||||
const isHierarchicalPartitionedContainer = (): boolean => collection.partitionKey?.kind === "MultiHash";
|
||||
|
||||
const getPartitionKeyValue = (): string => {
|
||||
return (collection.partitionKeyProperties || []).map((property) => "/" + property).join(", ");
|
||||
};
|
||||
|
||||
const partitionKeyName = "Partition key";
|
||||
const partitionKeyValue = getPartitionKeyValue();
|
||||
|
||||
const textHeadingStyle = {
|
||||
root: { fontWeight: FontWeights.semibold, fontSize: 16 },
|
||||
};
|
||||
|
||||
const textSubHeadingStyle = {
|
||||
root: { fontWeight: FontWeights.semibold },
|
||||
};
|
||||
|
||||
const startPollingforUpdate = (currentJob: DataTransferJobGetResults) => {
|
||||
if (isCurrentJobInProgress(currentJob)) {
|
||||
const jobName = currentJob?.properties?.jobName;
|
||||
try {
|
||||
pollDataTransferJob(
|
||||
jobName,
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
userContext.databaseAccount.name,
|
||||
);
|
||||
} catch (error) {
|
||||
handleError(error, "ChangePartitionKey", `Failed to complete data transfer job ${jobName}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const cancelRunningDataTransferJob = async (currentJob: DataTransferJobGetResults) => {
|
||||
await cancelDataTransferJob(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
userContext.databaseAccount.name,
|
||||
currentJob?.properties?.jobName,
|
||||
);
|
||||
};
|
||||
|
||||
const isCurrentJobInProgress = (currentJob: DataTransferJobGetResults) => {
|
||||
const jobStatus = currentJob?.properties?.status;
|
||||
return (
|
||||
jobStatus &&
|
||||
jobStatus !== "Completed" &&
|
||||
jobStatus !== "Cancelled" &&
|
||||
jobStatus !== "Failed" &&
|
||||
jobStatus !== "Faulted"
|
||||
);
|
||||
};
|
||||
|
||||
const refreshDataTransferOperations = async () => {
|
||||
await refreshDataTransferJobs(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
userContext.databaseAccount.name,
|
||||
);
|
||||
};
|
||||
|
||||
const findPortalDataTransferJob = (): DataTransferJobGetResults => {
|
||||
return dataTransferJobs.find((feed: DataTransferJobGetResults) => {
|
||||
const sourceSink: CosmosSqlDataTransferDataSourceSink = feed?.properties
|
||||
?.source as CosmosSqlDataTransferDataSourceSink;
|
||||
return sourceSink.databaseName === collection.databaseId && sourceSink.containerName === collection.id();
|
||||
});
|
||||
};
|
||||
|
||||
const getProgressDescription = (): string => {
|
||||
const processedCount = portalDataTransferJob?.properties?.processedCount;
|
||||
const totalCount = portalDataTransferJob?.properties?.totalCount;
|
||||
const processedCountString = totalCount > 0 ? `(${processedCount} of ${totalCount} documents processed)` : "";
|
||||
return `${portalDataTransferJob?.properties?.status} ${processedCountString}`;
|
||||
};
|
||||
|
||||
const startPartitionkeyChangeWorkflow = () => {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Change partition key",
|
||||
<ChangePartitionKeyPane
|
||||
sourceDatabase={database}
|
||||
sourceCollection={collection}
|
||||
explorer={explorer}
|
||||
onClose={refreshDataTransferOperations}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
const getPercentageComplete = () => {
|
||||
const jobStatus = portalDataTransferJob?.properties?.status;
|
||||
const isCompleted = jobStatus === "Completed";
|
||||
if (isCompleted) {
|
||||
return 1;
|
||||
}
|
||||
const processedCount = portalDataTransferJob?.properties?.processedCount;
|
||||
const totalCount = portalDataTransferJob?.properties?.totalCount;
|
||||
const isJobInProgress = isCurrentJobInProgress(portalDataTransferJob);
|
||||
return isJobInProgress ? (totalCount > 0 ? processedCount / totalCount : null) : 0;
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 20 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
<Text styles={textHeadingStyle}>Change {partitionKeyName.toLowerCase()}</Text>
|
||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={textSubHeadingStyle}>Current {partitionKeyName.toLowerCase()}</Text>
|
||||
<Text styles={textSubHeadingStyle}>Partitioning</Text>
|
||||
</Stack>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text>{partitionKeyValue}</Text>
|
||||
<Text>{isHierarchicalPartitionedContainer() ? "Hierarchical" : "Non-hierarchical"}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<MessageBar messageBarType={MessageBarType.warning}>
|
||||
To safeguard the integrity of the data being copied to the new container, ensure that no updates are made to the
|
||||
source container for the entire duration of the partition key change process.
|
||||
<Link
|
||||
href="https://learn.microsoft.com/azure/cosmos-db/container-copy#how-does-container-copy-work"
|
||||
target="_blank"
|
||||
underline
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
</MessageBar>
|
||||
<Text>
|
||||
To change the partition key, a new destination container must be created or an existing destination container
|
||||
selected. Data will then be copied to the destination container.
|
||||
</Text>
|
||||
<PrimaryButton
|
||||
styles={{ root: { width: "fit-content" } }}
|
||||
text="Change"
|
||||
onClick={startPartitionkeyChangeWorkflow}
|
||||
disabled={isCurrentJobInProgress(portalDataTransferJob)}
|
||||
/>
|
||||
{portalDataTransferJob && (
|
||||
<Stack>
|
||||
<Text styles={textHeadingStyle}>{partitionKeyName} change job</Text>
|
||||
<Stack
|
||||
horizontal
|
||||
tokens={{ childrenGap: 20 }}
|
||||
styles={{
|
||||
root: {
|
||||
alignItems: "center",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ProgressIndicator
|
||||
label={portalDataTransferJob?.properties?.jobName}
|
||||
description={getProgressDescription()}
|
||||
percentComplete={getPercentageComplete()}
|
||||
styles={{
|
||||
root: {
|
||||
width: "85%",
|
||||
},
|
||||
}}
|
||||
></ProgressIndicator>
|
||||
{isCurrentJobInProgress(portalDataTransferJob) && (
|
||||
<DefaultButton text="Cancel" onClick={() => cancelRunningDataTransferJob(portalDataTransferJob)} />
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user