Compare commits

..

3 Commits

Author SHA1 Message Date
sunghyunkang1111
06973bd3d5 Update snapshots 2023-10-06 12:43:57 -05:00
sunghyunkang1111
44667beba9 take out toggle button when closing tab 2023-10-06 12:28:01 -05:00
sunghyunkang1111
0566f19e87 Add copilot toggle button 2023-10-06 11:36:15 -05:00
218 changed files with 26019 additions and 40153 deletions

View File

@@ -145,5 +145,4 @@ src/Explorer/Notebook/temp/inputs/connected-editors/codemirror.tsx
src/Explorer/Tree/ResourceTreeAdapter.tsx src/Explorer/Tree/ResourceTreeAdapter.tsx
__mocks__/monaco-editor.ts __mocks__/monaco-editor.ts
src/Explorer/Tree/ResourceTree.tsx src/Explorer/Tree/ResourceTree.tsx
src/Utils/EndpointUtils.ts
src/Utils/PriorityBasedExecutionUtils.ts src/Utils/PriorityBasedExecutionUtils.ts

View File

@@ -53,9 +53,4 @@ module.exports = {
}, },
], ],
}, },
settings: {
react: {
version: "detect",
},
},
}; };

View File

@@ -14,11 +14,11 @@ jobs:
name: "Log Code Metrics" name: "Log Code Metrics"
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: node utils/codeMetrics.js - run: node utils/codeMetrics.js
env: env:
@@ -27,11 +27,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: "Compile TypeScript" name: "Compile TypeScript"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run compile - run: npm run compile
- run: npm run compile:strict - run: npm run compile:strict
@@ -39,44 +39,44 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: "Check Format" name: "Check Format"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run format:check - run: npm run format:check
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: "Lint" name: "Lint"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run lint - run: npm run lint
unittest: unittest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: "Unit Tests" name: "Unit Tests"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run test - run: npm run test
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: "Build" name: "Build"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run build:contracts - run: npm run build:contracts
- name: Restore Build Cache - name: Restore Build Cache
@@ -86,10 +86,10 @@ jobs:
key: ${{ runner.os }}-build-cache key: ${{ runner.os }}-build-cache
- run: npm run pack:prod - run: npm run pack:prod
env: env:
NODE_OPTIONS: "--max-old-space-size=4096" NODE_OPTIONS: '--max-old-space-size=4096'
- run: cp -r ./Contracts ./dist/contracts - run: cp -r ./Contracts ./dist/contracts
- run: cp -r ./configs ./dist/configs - run: cp -r ./configs ./dist/configs
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
with: with:
name: dist name: dist
path: dist/ path: dist/
@@ -107,11 +107,11 @@ jobs:
if: false if: false
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- uses: southpolesteve/cosmos-emulator-github-action@v1 - uses: southpolesteve/cosmos-emulator-github-action@v1
- name: End to End Tests - name: End to End Tests
run: | run: |
@@ -124,7 +124,7 @@ jobs:
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator" DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator"
PLATFORM: "Emulator" PLATFORM: "Emulator"
NODE_TLS_REJECT_UNAUTHORIZED: 0 NODE_TLS_REJECT_UNAUTHORIZED: 0
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:
name: screenshots name: screenshots
@@ -149,11 +149,11 @@ jobs:
- ./test/sql/resourceToken.spec.ts - ./test/sql/resourceToken.spec.ts
- ./test/tables/container.spec.ts - ./test/tables/container.spec.ts
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v4 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm start & - run: npm start &
- run: npm run wait-for-server - run: npm run wait-for-server
@@ -162,7 +162,7 @@ jobs:
# Run tests up to three times # 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) 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 shell: bash
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:
name: screenshots name: screenshots
@@ -180,14 +180,14 @@ jobs:
with: with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }} nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Download Dist Folder - name: Download Dist Folder
uses: actions/download-artifact@v3 uses: actions/download-artifact@v2
with: with:
name: dist name: dist
- run: cp ./configs/prod.json config.json - 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 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 pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg - run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
name: packages name: packages
with: with:
path: "*.nupkg" path: "*.nupkg"
@@ -204,7 +204,7 @@ jobs:
with: with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }} nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Download Dist Folder - name: Download Dist Folder
uses: actions/download-artifact@v3 uses: actions/download-artifact@v2
with: with:
name: dist name: dist
- run: cp ./configs/mpac.json config.json - 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 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 pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg - run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
name: packages name: packages
with: with:
path: "*.nupkg" path: "*.nupkg"

View File

@@ -20,9 +20,9 @@ jobs:
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }} NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 18.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 18.x node-version: 14.x
- run: npm ci - run: npm ci
- run: node utils/cleanupDBs.js - run: node utils/cleanupDBs.js

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

View File

@@ -147,7 +147,6 @@
// CommandBar // CommandBar
@CommandBarButtonHeight: 40px; @CommandBarButtonHeight: 40px;
@FabricCommandBarButtonHeight: 34px;
/********************************************************************************** /**********************************************************************************
Portal Consts Portal Consts
@@ -165,7 +164,7 @@
@FabricFont: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif; @FabricFont: "Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
@FabricBoxBorderRadius: 8px; @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; @FabricBoxMargin: 4px 3px 4px 3px;
@FabricAccentMediumHigh: #0c695a; @FabricAccentMediumHigh: #0c695a;

View File

@@ -1179,16 +1179,16 @@ menuQuickStart {
} }
} }
#tbodycontent tr.gridRowSelected { .gridRowSelected {
.active(); .active();
} }
#tbodycontent tr.gridRowSelected:hover { .gridRowSelected:hover {
cursor: default; cursor: default;
.hover(); .hover();
} }
#tbodycontent tr.gridRowHighlighted { .gridRowHighlighted {
border-style: dotted; border-style: dotted;
border-width: 2px; border-width: 2px;
} }
@@ -2576,10 +2576,9 @@ a:link {
.querydropdown.placeholderVisible { .querydropdown.placeholderVisible {
font-style: italic; font-style: italic;
} }
.querydropdown.placeholderVisible::placeholder { .querydropdown.placeholderVisible::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
/* Chrome, Firefox, Opera, Safari 10.1+ */
color: #767474; color: #767474;
opacity: 1; opacity: 1;
} }
.querydropdown:hover { .querydropdown:hover {
@@ -2649,7 +2648,7 @@ a:link {
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .tabNavText { .nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .tabNavText {
font-weight: bolder; font-weight: bolder;
border-bottom: 2px solid rgba(0, 120, 212, 1); border-bottom: 2px solid rgba(0,120,212,1);
} }
.nav-tabs > li.active:focus > .tabNavContentContainer { .nav-tabs > li.active:focus > .tabNavContentContainer {
@@ -2897,21 +2896,9 @@ a:link {
padding-left: 8px; padding-left: 8px;
} }
.settingsSectionInlineCheckbox {
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
align-items: center;
gap: 5px;
}
.settingsSectionLabel { .settingsSectionLabel {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;
margin-right: 5px; margin-right: 5px;
.panelInfoIcon {
margin-left: 5px;
}
} }
.pageOptionsPart { .pageOptionsPart {
@@ -3109,3 +3096,4 @@ a:link {
background: white; background: white;
height: 100%; height: 100%;
} }

View File

@@ -25,38 +25,33 @@ a:focus {
} }
.resourceTreeAndTabs { .resourceTreeAndTabs {
border-radius: 0px; border-radius: @FabricBoxBorderRadius;
box-shadow: @FabricBoxBorderShadow; box-shadow: @FabricBoxBorderShadow;
margin: @FabricBoxMargin; margin: @FabricBoxMargin;
margin-top: 0px; margin-top: 4px;
margin-bottom: 0px;
background-color: #ffffff; background-color: #ffffff;
} }
.tabsManagerContainer { .tabsManagerContainer {
background-color: #ffffff background-color: #fafafa
} }
.nav-tabs-margin { .nav-tabs-margin {
padding-top: 8px; padding-top: 8px;
background-color: #ffffff background-color: #fafafa
} }
.commandBarContainer { .commandBarContainer {
background-color: #ffffff; background-color: #ffffff;
border-radius: @FabricBoxBorderRadius @FabricBoxBorderRadius 0px 0px; border-bottom: none;
border-radius: @FabricBoxBorderRadius;
box-shadow: @FabricBoxBorderShadow; box-shadow: @FabricBoxBorderShadow;
margin: @FabricBoxMargin; margin: @FabricBoxMargin;
margin-top: 0px;
margin-bottom: 0px;
padding-top: 2px; padding-top: 2px;
padding: 0px;
height: 40px;
} }
.dividerContainer { .dividerContainer {
padding: @SmallSpace 0px @SmallSpace 0px; padding: @SmallSpace 0px @SmallSpace 0px;
height: @FabricCommandBarButtonHeight;
.flex-display(); .flex-display();
span { span {
@@ -163,10 +158,9 @@ a:focus {
.dataExplorerErrorConsoleContainer { .dataExplorerErrorConsoleContainer {
border-radius: 0px 0px @FabricBoxBorderRadius @FabricBoxBorderRadius; border-radius: @FabricBoxBorderRadius;
box-shadow: @FabricBoxBorderShadow; box-shadow: @FabricBoxBorderShadow;
margin: @FabricBoxMargin; margin: @FabricBoxMargin;
margin-top: 0px;
width: auto; width: auto;
align-self: auto; align-self: auto;
} }

52191
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,19 +5,19 @@
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"@azure/arm-cosmosdb": "9.1.0", "@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "4.0.1-beta.2", "@azure/cosmos": "3.16.2",
"@azure/cosmos-language-service": "0.0.5", "@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.2.1", "@azure/identity": "1.2.1",
"@azure/ms-rest-nodeauth": "3.0.7", "@azure/ms-rest-nodeauth": "3.0.7",
"@azure/msal-browser": "2.14.2", "@azure/msal-browser": "2.14.2",
"@babel/plugin-proposal-class-properties": "7.12.1", "@babel/plugin-proposal-class-properties": "7.12.1",
"@babel/plugin-proposal-decorators": "7.12.12", "@babel/plugin-proposal-decorators": "7.12.12",
"@fluentui/react": "8.112.1", "@fluentui/react": "8.14.3",
"@fluentui/react-components": "9.34.0", "@fluentui/react-components": "9.32.1",
"@jupyterlab/services": "6.0.2", "@jupyterlab/services": "6.0.2",
"@jupyterlab/terminal": "3.0.3", "@jupyterlab/terminal": "3.0.3",
"@microsoft/applicationinsights-web": "2.6.1", "@microsoft/applicationinsights-web": "2.6.1",
"@nteract/commutable": "7.5.1", "@nteract/commutable": "7.4.2",
"@nteract/connected-components": "6.8.2", "@nteract/connected-components": "6.8.2",
"@nteract/core": "15.1.0", "@nteract/core": "15.1.0",
"@nteract/data-explorer": "8.0.3", "@nteract/data-explorer": "8.0.3",
@@ -49,20 +49,20 @@
"applicationinsights": "1.8.0", "applicationinsights": "1.8.0",
"bootstrap": "3.4.1", "bootstrap": "3.4.1",
"canvas": "file:./canvas", "canvas": "file:./canvas",
"clean-webpack-plugin": "4.0.0", "clean-webpack-plugin": "3.0.0",
"clipboard-copy": "4.0.1", "clipboard-copy": "4.0.1",
"copy-webpack-plugin": "11.0.0", "copy-webpack-plugin": "9.0.1",
"crossroads": "0.12.2", "crossroads": "0.12.2",
"css-element-queries": "1.1.1", "css-element-queries": "1.1.1",
"d3": "6.1.1", "d3": "6.1.1",
"datatables.net-colreorder-dt": "1.7.0", "datatables.net-colreorder-dt": "1.5.1",
"datatables.net-dt": "1.13.8", "datatables.net-dt": "1.10.19",
"date-fns": "1.29.0", "date-fns": "1.29.0",
"dayjs": "1.8.19", "dayjs": "1.8.19",
"dom-to-image": "2.6.0", "dom-to-image": "2.6.0",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"eslint-plugin-jest": "27.4.2", "eslint-plugin-jest": "23.13.2",
"eslint-plugin-react": "7.33.2", "eslint-plugin-react": "7.20.0",
"hasher": "1.2.0", "hasher": "1.2.0",
"html2canvas": "1.0.0-rc.5", "html2canvas": "1.0.0-rc.5",
"i18next": "19.8.4", "i18next": "19.8.4",
@@ -71,15 +71,14 @@
"iframe-resizer-react": "1.1.0", "iframe-resizer-react": "1.1.0",
"immutable": "4.0.0-rc.12", "immutable": "4.0.0-rc.12",
"is-ci": "2.0.0", "is-ci": "2.0.0",
"jquery": "3.7.1", "jquery": "3.5.1",
"jquery-typeahead": "2.11.1", "jquery-typeahead": "2.10.6",
"jquery-ui-dist": "1.13.2", "jquery-ui-dist": "1.12.1",
"knockout": "3.5.1", "knockout": "3.5.1",
"mkdirp": "1.0.4", "mkdirp": "1.0.4",
"monaco-editor": "0.44.0", "monaco-editor": "0.18.1",
"ms": "2.1.3", "ms": "2.1.3",
"patch-package": "8.0.0", "p-retry": "4.2.0",
"p-retry": "4.6.2",
"plotly.js-cartesian-dist-min": "1.52.3", "plotly.js-cartesian-dist-min": "1.52.3",
"post-robot": "10.0.42", "post-robot": "10.0.42",
"q": "1.5.1", "q": "1.5.1",
@@ -87,20 +86,21 @@
"react-animate-height": "2.0.8", "react-animate-height": "2.0.8",
"react-dnd": "14.0.2", "react-dnd": "14.0.2",
"react-dnd-html5-backend": "14.0.0", "react-dnd-html5-backend": "14.0.0",
"react-dom": "16.14.0", "react-dom": "16.13.1",
"react-hotkeys": "2.0.0", "react-hotkeys": "2.0.0",
"react-i18next": "11.8.5", "react-i18next": "11.8.5",
"react-notification-system": "0.2.17", "react-notification-system": "0.2.17",
"react-redux": "7.1.3", "react-redux": "7.1.3",
"react-splitter-layout": "4.0.0", "react-splitter-layout": "4.0.0",
"react-string-format": "1.0.1",
"react-youtube": "9.0.1", "react-youtube": "9.0.1",
"redux": "4.0.4",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12", "rx-jupyter": "5.5.12",
"rxjs": "6.6.3",
"sanitize-html": "2.3.3", "sanitize-html": "2.3.3",
"styled-components": "5.0.1", "styled-components": "4.3.2",
"swr": "0.4.0", "swr": "0.4.0",
"terser-webpack-plugin": "5.3.9", "terser-webpack-plugin": "5.1.4",
"underscore": "1.9.1", "underscore": "1.9.1",
"utility-types": "3.10.0", "utility-types": "3.10.0",
"zustand": "3.5.0" "zustand": "3.5.0"
@@ -115,14 +115,11 @@
"@types/codemirror": "0.0.56", "@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30", "@types/crossroads": "0.0.30",
"@types/d3": "5.9.2", "@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/dom-to-image": "2.6.2",
"@types/enzyme": "3.10.7", "@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.6", "@types/enzyme-adapter-react-16": "1.0.6",
"@types/hasher": "0.0.31", "@types/hasher": "0.0.31",
"@types/jest": "26.0.20", "@types/jest": "26.0.20",
"@types/jquery": "3.5.29",
"@types/node": "12.11.1", "@types/node": "12.11.1",
"@types/post-robot": "10.0.1", "@types/post-robot": "10.0.1",
"@types/q": "1.5.1", "@types/q": "1.5.1",
@@ -136,62 +133,61 @@
"@types/styled-components": "5.1.1", "@types/styled-components": "5.1.1",
"@types/underscore": "1.7.36", "@types/underscore": "1.7.36",
"@types/youtube-player": "5.5.6", "@types/youtube-player": "5.5.6",
"@typescript-eslint/eslint-plugin": "6.7.4", "@typescript-eslint/eslint-plugin": "4.22.0",
"@typescript-eslint/parser": "6.7.4", "@typescript-eslint/parser": "4.22.0",
"@webpack-cli/serve": "2.0.5", "@webpack-cli/serve": "1.5.2",
"babel-jest": "24.9.0", "babel-jest": "24.9.0",
"babel-loader": "8.1.0", "babel-loader": "8.1.0",
"buffer": "5.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", "create-file-webpack": "1.0.2",
"css-loader": "6.8.1", "css-loader": "1.0.0",
"enzyme": "3.11.0", "enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.5", "enzyme-adapter-react-16": "1.15.5",
"enzyme-to-json": "3.6.1", "enzyme-to-json": "3.6.1",
"eslint": "8.50.0", "eslint": "7.8.1",
"eslint-cli": "1.1.1", "eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2", "eslint-plugin-no-null": "1.0.2",
"eslint-plugin-prefer-arrow": "1.2.3", "eslint-plugin-prefer-arrow": "1.2.2",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.2.0",
"expect-playwright": "0.3.3", "expect-playwright": "0.3.3",
"fast-glob": "3.2.5", "fast-glob": "3.2.5",
"file-loader": "2.0.0",
"fs-extra": "7.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": "0.5.5",
"html-loader-jest": "0.2.1", "html-loader-jest": "0.2.1",
"html-webpack-plugin": "5.5.3", "html-webpack-plugin": "5.3.2",
"jest": "26.6.3", "jest": "26.6.3",
"jest-canvas-mock": "2.3.1", "jest-canvas-mock": "2.3.1",
"jest-playwright-preset": "1.5.1", "jest-playwright-preset": "1.5.1",
"jest-react-hooks-shallow": "1.5.1", "jest-react-hooks-shallow": "1.5.1",
"jest-trx-results-processor": "0.0.7", "jest-trx-results-processor": "0.0.7",
"less": "3.8.1", "less": "3.8.1",
"less-loader": "11.1.3", "less-loader": "4.1.0",
"less-vars-loader": "1.1.0", "less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "2.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", "node-fetch": "2.6.1",
"playwright": "1.13.0", "playwright": "1.13.0",
"prettier": "3.0.3", "prettier": "3.0.3",
"process": "0.11.10", "process": "0.11.10",
"querystring-es3": "0.2.1",
"raw-loader": "0.5.1", "raw-loader": "0.5.1",
"react-dev-utils": "11.0.4", "react-dev-utils": "11.0.4",
"rimraf": "3.0.0", "rimraf": "3.0.0",
"sinon": "3.2.1", "sinon": "3.2.1",
"style-loader": "0.23.0", "style-loader": "0.23.0",
"ts-loader": "9.2.4", "ts-loader": "9.2.4",
"typedoc": "0.21.5", "typedoc": "0.20.36",
"typescript": "4.3.5", "typescript": "4.3.4",
"url-loader": "4.1.1", "url-loader": "1.1.1",
"wait-on": "4.0.2", "wait-on": "4.0.2",
"webpack": "5.88.2", "webpack": "5.47.0",
"webpack-bundle-analyzer": "4.9.1", "webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "5.1.4", "webpack-cli": "4.7.2",
"webpack-dev-server": "4.15.1" "webpack-dev-server": "3.11.2"
}, },
"scripts": { "scripts": {
"postinstall": "patch-package",
"start": "webpack serve --mode development", "start": "webpack serve --mode development",
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build", "dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
"build:dataExplorer:ci": "npm run build:ci", "build:dataExplorer:ci": "npm run build:ci",

View File

@@ -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);
/**

View File

@@ -145,9 +145,6 @@ export class Queries {
public static QueryEditorMinHeightRatio: number = 0.1; public static QueryEditorMinHeightRatio: number = 0.1;
public static QueryEditorMaxHeightRatio: number = 0.4; public static QueryEditorMaxHeightRatio: number = 0.4;
public static readonly DefaultMaxDegreeOfParallelism = 6; public static readonly DefaultMaxDegreeOfParallelism = 6;
public static readonly DefaultRetryAttempts = 9;
public static readonly DefaultRetryIntervalInMs = 0;
public static readonly DefaultMaxWaitTimeInSeconds = 30;
} }
export class SavedQueries { export class SavedQueries {
@@ -174,7 +171,6 @@ export class Areas {
public static Tab: string = "Tab"; public static Tab: string = "Tab";
public static ShareDialog: string = "Share Access Dialog"; public static ShareDialog: string = "Share Access Dialog";
public static Notebook: string = "Notebook"; public static Notebook: string = "Notebook";
public static Copilot: string = "Copilot";
} }
export class HttpHeaders { export class HttpHeaders {
@@ -211,10 +207,6 @@ export class HttpHeaders {
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot"; public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
} }
export class ContentType {
public static applicationJson: string = "application/json";
}
export class ApiType { export class ApiType {
// Mapped to hexadecimal values in the backend // Mapped to hexadecimal values in the backend
public static readonly MongoDB: number = 1; public static readonly MongoDB: number = 1;
@@ -435,22 +427,6 @@ export class JunoEndpoints {
public static readonly Stage = "https://tools-staging.cosmos.azure.com"; public static readonly Stage = "https://tools-staging.cosmos.azure.com";
} }
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";
}
export class PriorityLevel { export class PriorityLevel {
public static readonly High = "high"; public static readonly High = "high";
public static readonly Low = "low"; public static readonly Low = "low";

View File

@@ -125,7 +125,7 @@ describe("requestPlugin", () => {
const headers = {}; const headers = {};
const endpoint = "https://docs.azure.com"; const endpoint = "https://docs.azure.com";
const path = "/dbs/foo"; const path = "/dbs/foo";
requestPlugin({ endpoint, headers, path } as any, undefined, next as any); requestPlugin({ endpoint, headers, path } as any, next as any);
expect(next.mock.calls[0][0]).toMatchSnapshot(); expect(next.mock.calls[0][0]).toMatchSnapshot();
}); });
}); });
@@ -137,7 +137,7 @@ describe("requestPlugin", () => {
const headers = {}; const headers = {};
const endpoint = ""; const endpoint = "";
const path = "/dbs/foo"; const path = "/dbs/foo";
requestPlugin({ endpoint, headers, path } as any, undefined, next as any); requestPlugin({ endpoint, headers, path } as any, next as any);
expect(next.mock.calls[0][0]).toMatchSnapshot(); expect(next.mock.calls[0][0]).toMatchSnapshot();
}); });
}); });

View File

@@ -1,17 +1,13 @@
import * as Cosmos from "@azure/cosmos"; import * as Cosmos from "@azure/cosmos";
import { sendCachedDataMessage } from "Common/MessageHandler"; import { configContext, Platform } from "../ConfigContext";
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
import { AuthorizationToken, MessageTypes } 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 { Platform, configContext } from "../ConfigContext";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils"; import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
import { EmulatorMasterKey, HttpHeaders } from "./Constants"; import { EmulatorMasterKey, HttpHeaders } from "./Constants";
import { getErrorMessage } from "./ErrorHandlingUtils"; import { getErrorMessage } from "./ErrorHandlingUtils";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { PriorityLevel } from "../Common/Constants";
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
import { AuthType } from "../AuthType";
const _global = typeof self === "undefined" ? window : self; const _global = typeof self === "undefined" ? window : self;
@@ -30,39 +26,6 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
return decodeURIComponent(headers.authorization); return decodeURIComponent(headers.authorization);
} }
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:
// 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);
}
}
if (userContext.masterKey) { if (userContext.masterKey) {
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK. // TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey); await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
@@ -78,7 +41,7 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
return decodeURIComponent(result.PrimaryReadWriteToken); return decodeURIComponent(result.PrimaryReadWriteToken);
}; };
export const requestPlugin: Cosmos.Plugin<any> = async (requestContext, diagnosticNode, next) => { export const requestPlugin: Cosmos.Plugin<any> = async (requestContext, next) => {
requestContext.endpoint = new URL(configContext.PROXY_PATH, window.location.href).href; requestContext.endpoint = new URL(configContext.PROXY_PATH, window.location.href).href;
requestContext.headers["x-ms-proxy-target"] = endpoint(); requestContext.headers["x-ms-proxy-target"] = endpoint();
return next(requestContext); return next(requestContext);
@@ -93,11 +56,7 @@ export const endpoint = () => {
return userContext.endpoint || userContext?.databaseAccount?.properties?.documentEndpoint; return userContext.endpoint || userContext?.databaseAccount?.properties?.documentEndpoint;
}; };
export async function getTokenFromAuthService( export async function getTokenFromAuthService(verb: string, resourceType: string, resourceId?: string): Promise<any> {
verb: string,
resourceType: string,
resourceId?: string,
): Promise<AuthorizationToken> {
try { try {
const host = configContext.BACKEND_ENDPOINT; const host = configContext.BACKEND_ENDPOINT;
const response = await _global.fetch(host + "/api/guest/runtimeproxy/authorizationTokens", { const response = await _global.fetch(host + "/api/guest/runtimeproxy/authorizationTokens", {
@@ -152,15 +111,11 @@ export function client(): Cosmos.CosmosClient {
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
key: userContext.masterKey, key: userContext.masterKey,
tokenProvider, tokenProvider,
connectionPolicy: {
enableEndpointDiscovery: false,
},
userAgentSuffix: "Azure Portal", userAgentSuffix: "Azure Portal",
defaultHeaders: _defaultHeaders, defaultHeaders: _defaultHeaders,
connectionPolicy: {
retryOptions: {
maxRetryAttemptCount: LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts),
fixedRetryIntervalInMilliseconds: LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval),
maxWaitTimeInSeconds: LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds),
},
},
}; };
if (configContext.PROXY_PATH !== undefined) { if (configContext.PROXY_PATH !== undefined) {

View File

@@ -1,4 +1,3 @@
import { QueryOperationOptions } from "@azure/cosmos";
import { QueryResults } from "../Contracts/ViewModels"; import { QueryResults } from "../Contracts/ViewModels";
interface QueryResponse { interface QueryResponse {
@@ -11,17 +10,13 @@ interface QueryResponse {
} }
export interface MinimalQueryIterator { export interface MinimalQueryIterator {
fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>; fetchNext: () => Promise<QueryResponse>;
} }
// Pick<QueryIterator<any>, "fetchNext">; // Pick<QueryIterator<any>, "fetchNext">;
export function nextPage( export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
documentsIterator: MinimalQueryIterator, return documentsIterator.fetchNext().then((response) => {
firstItemIndex: number,
queryOperationOptions?: QueryOperationOptions,
): Promise<QueryResults> {
return documentsIterator.fetchNext(queryOperationOptions).then((response) => {
const documents = response.resources; const documents = response.resources;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any const headers = (response as any).headers || {}; // TODO this is a private key. Remove any

View File

@@ -1,5 +1,7 @@
jest.mock("./MessageHandler"); jest.mock("./MessageHandler");
import { LogEntryLevel } from "../Contracts/Diagnostics";
import * as Logger from "./Logger"; import * as Logger from "./Logger";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { sendMessage } from "./MessageHandler"; import { sendMessage } from "./MessageHandler";
describe("Logger", () => { describe("Logger", () => {
@@ -9,16 +11,16 @@ describe("Logger", () => {
it("should log info messages", () => { it("should log info messages", () => {
Logger.logInfo("Test info", "DocDB"); Logger.logInfo("Test info", "DocDB");
expect(sendMessage).toHaveBeenCalled(); expect(sendMessage).toBeCalled();
}); });
it("should log error messages", () => { it("should log error messages", () => {
Logger.logError("Test error", "DocDB"); Logger.logError("Test error", "DocDB");
expect(sendMessage).toHaveBeenCalled(); expect(sendMessage).toBeCalled();
}); });
it("should log warnings", () => { it("should log warnings", () => {
Logger.logWarning("Test warning", "DocDB"); Logger.logWarning("Test warning", "DocDB");
expect(sendMessage).toHaveBeenCalled(); expect(sendMessage).toBeCalled();
}); });
}); });

View File

@@ -22,29 +22,20 @@ export function handleCachedDataMessage(message: any): void {
if (messageContent.error != null) { if (messageContent.error != null) {
cachedDataPromise.deferred.reject(messageContent.error); cachedDataPromise.deferred.reject(messageContent.error);
} else { } else {
cachedDataPromise.deferred.resolve(messageContent.data); cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data));
} }
runGarbageCollector(); 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>( export function sendCachedDataMessage<TResponseDataModel>(
messageType: MessageTypes, messageType: MessageTypes,
params: Object[], params: Object[],
scope?: string,
timeoutInMs?: number, timeoutInMs?: number,
): Q.Promise<TResponseDataModel> { ): Q.Promise<TResponseDataModel> {
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = { let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
deferred: Q.defer<TResponseDataModel>(), deferred: Q.defer<TResponseDataModel>(),
startTime: new Date(), startTime: new Date(),
id: _.uniqueId(scope), id: _.uniqueId(),
}; };
RequestMap[cachedDataPromise.id] = cachedDataPromise; RequestMap[cachedDataPromise.id] = cachedDataPromise;
sendMessage({ type: messageType, params: params, id: cachedDataPromise.id }); 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 { export function sendMessage(data: any): void {
_sendMessage({ _sendMessage({
signature: "pcIframe", signature: "pcIframe",

View File

@@ -1,10 +1,6 @@
import { Constants as CosmosSDKConstants } from "@azure/cosmos"; import { Constants as CosmosSDKConstants } from "@azure/cosmos";
import {
allowedMongoProxyEndpoints,
allowedMongoProxyEndpoints_ToBeDeprecated,
validateEndpoint,
} from "Utils/EndpointUtils";
import queryString from "querystring"; import queryString from "querystring";
import { allowedMongoProxyEndpoints, validateEndpoint } from "Utils/EndpointValidation";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import { configContext } from "../ConfigContext"; import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
@@ -14,7 +10,7 @@ import DocumentId from "../Explorer/Tree/DocumentId";
import { hasFlag } from "../Platform/Hosted/extractFeatures"; import { hasFlag } from "../Platform/Hosted/extractFeatures";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils"; import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { ApiType, ContentType, HttpHeaders, HttpStatusCodes, MongoProxyEndpoints } from "./Constants"; import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
import { MinimalQueryIterator } from "./IteratorUtilities"; import { MinimalQueryIterator } from "./IteratorUtilities";
import { sendMessage } from "./MessageHandler"; import { sendMessage } from "./MessageHandler";
@@ -66,73 +62,6 @@ export function queryDocuments(
isResourceList: boolean, isResourceList: boolean,
query: string, query: string,
continuationToken?: 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> { ): Promise<QueryResponse> {
const { databaseAccount } = userContext; const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint; const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -193,54 +122,6 @@ export function readDocument(
databaseId: string, databaseId: string,
collection: Collection, collection: Collection,
documentId: DocumentId, 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> { ): Promise<DataModels.DocumentId> {
const { databaseAccount } = userContext; const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint; const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -288,51 +169,6 @@ export function createDocument(
collection: Collection, collection: Collection,
partitionKeyProperty: string, partitionKeyProperty: string,
documentContent: unknown, 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> { ): Promise<DataModels.DocumentId> {
const { databaseAccount } = userContext; const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint; const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -372,56 +208,6 @@ export function updateDocument(
collection: Collection, collection: Collection,
documentId: DocumentId, documentId: DocumentId,
documentContent: string, 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> { ): Promise<DataModels.DocumentId> {
const { databaseAccount } = userContext; const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint; const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -451,7 +237,7 @@ export function updateDocument_ToBeDeprecated(
headers: { headers: {
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson, [HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()), [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> { 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 { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint; const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const idComponents = documentId.self.split("/"); const idComponents = documentId.self.split("/");
@@ -538,7 +277,7 @@ export function deleteDocument_ToBeDeprecated(
headers: { headers: {
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson, [HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()), [CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
}, },
}) })
@@ -552,52 +291,6 @@ export function deleteDocument_ToBeDeprecated(
export function createMongoCollectionWithProxy( export function createMongoCollectionWithProxy(
params: DataModels.CreateCollectionParams, 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> { ): Promise<DataModels.Collection> {
const { databaseAccount } = userContext; const { databaseAccount } = userContext;
const shardKey: string = params.partitionKey?.paths[0]; const shardKey: string = params.partitionKey?.paths[0];
@@ -641,20 +334,13 @@ export function createMongoCollectionWithProxy_ToBeDeprecated(
return await errorHandling(response, "creating collection", mongoParams); return await errorHandling(response, "creating collection", mongoParams);
}); });
} }
export function getFeatureEndpointOrDefault(feature: string): string { export function getFeatureEndpointOrDefault(feature: string): string {
let endpoint; const endpoint =
if (useMongoProxyEndpoint(feature)) { hasFlag(userContext.features.mongoProxyAPIs, feature) &&
endpoint = configContext.MONGO_PROXY_ENDPOINT; validateEndpoint(userContext.features.mongoProxyEndpoint, allowedMongoProxyEndpoints)
} else { ? userContext.features.mongoProxyEndpoint
endpoint = : configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
hasFlag(userContext.features.mongoProxyAPIs, feature) &&
validateEndpoint(userContext.features.mongoProxyEndpoint, [
...allowedMongoProxyEndpoints,
...allowedMongoProxyEndpoints_ToBeDeprecated,
])
? userContext.features.mongoProxyEndpoint
: configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
}
return getEndpoint(endpoint); return getEndpoint(endpoint);
} }
@@ -663,11 +349,7 @@ export function getEndpoint(endpoint: string): string {
let url = endpoint + "/api/mongo/explorer"; let url = endpoint + "/api/mongo/explorer";
if (userContext.authType === AuthType.EncryptedToken) { if (userContext.authType === AuthType.EncryptedToken) {
if (endpoint === configContext.MONGO_PROXY_ENDPOINT) { url = url.replace("api/mongo", "api/guest/mongo");
url = url.replace("api/mongo", "api/connectionstring/mongo");
} else {
url = url.replace("api/mongo", "api/guest/mongo");
}
} }
return url; return url;
} }
@@ -688,16 +370,3 @@ async function errorHandling(response: Response, action: string, params: unknown
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string { 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}`; 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 {
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) &&
[MongoProxyEndpoints.Development, MongoProxyEndpoints.Mpac].includes(configContext.MONGO_PROXY_ENDPOINT)
);
}

View File

@@ -1,4 +1,3 @@
import { isServerlessAccount } from "Utils/CapabilityUtils";
import * as _ from "underscore"; import * as _ from "underscore";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
@@ -9,11 +8,12 @@ import { useDatabases } from "../Explorer/useDatabases";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants"; import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
import { handleError } from "./ErrorHandlingUtils";
import { createCollection } from "./dataAccess/createCollection"; import { createCollection } from "./dataAccess/createCollection";
import { createDocument } from "./dataAccess/createDocument"; import { createDocument } from "./dataAccess/createDocument";
import { deleteDocument } from "./dataAccess/deleteDocument"; import { deleteDocument } from "./dataAccess/deleteDocument";
import { queryDocuments } from "./dataAccess/queryDocuments"; import { queryDocuments } from "./dataAccess/queryDocuments";
import { handleError } from "./ErrorHandlingUtils";
import { isServerlessAccount } from "Utils/CapabilityUtils";
export class QueriesClient { export class QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = { private static readonly PartitionKey: DataModels.PartitionKey = {

View File

@@ -146,7 +146,6 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
onClick={onEditEntity} onClick={onEditEntity}
tabIndex={0} tabIndex={0}
onKeyPress={handleKeyPress} onKeyPress={handleKeyPress}
role="button"
/> />
</div> </div>
</TooltipHost> </TooltipHost>
@@ -161,7 +160,6 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
onClick={onDeleteEntity} onClick={onDeleteEntity}
tabIndex={0} tabIndex={0}
onKeyPress={handleKeyPressdelete} onKeyPress={handleKeyPressdelete}
role="button"
/> />
</TooltipHost> </TooltipHost>
)} )}

View File

@@ -1,188 +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" || 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`);
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}`);
}
};

View File

@@ -1,5 +1,5 @@
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
import { getCommonQueryOptions } from "./queryDocuments"; import { getCommonQueryOptions } from "./queryDocuments";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
describe("getCommonQueryOptions", () => { describe("getCommonQueryOptions", () => {
it("builds the correct default options objects", () => { it("builds the correct default options objects", () => {

View File

@@ -26,5 +26,6 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) || (storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Queries.itemsPerPage; Queries.itemsPerPage;
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism); options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
return options; return options;
}; };

View File

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

View File

@@ -1,57 +1,18 @@
import { ContainerResponse } from "@azure/cosmos";
import { Queries } from "Common/Constants"; import { Queries } from "Common/Constants";
import { Platform, configContext } from "ConfigContext";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { listCassandraTables } from "../../Utils/arm/generatedClients/cosmos/cassandraResources"; import { listCassandraTables } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/cosmos/gremlinResources"; import { listGremlinGraphs } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources"; import { listMongoDBCollections } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { listSqlContainers } from "../../Utils/arm/generatedClients/cosmos/sqlResources"; import { listSqlContainers } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { listTables } from "../../Utils/arm/generatedClients/cosmos/tableResources"; import { listTables } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> { export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`); 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().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 { try {
if ( if (
userContext.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&

View File

@@ -1,22 +1,15 @@
import { Platform, configContext } from "ConfigContext";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels"; import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/cosmos/cassandraResources"; import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/gremlinResources"; import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources"; import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/sqlResources"; import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { readOfferWithSDK } from "./readOfferWithSDK"; import { readOfferWithSDK } from "./readOfferWithSDK";
export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promise<Offer> => { 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}`); const clearMessage = logConsoleProgress(`Querying offer for database ${params.databaseId}`);
try { try {

View File

@@ -1,53 +1,17 @@
import { Platform, configContext } from "ConfigContext";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/cosmos/cassandraResources"; import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/cosmos/gremlinResources"; import { listGremlinDatabases } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources"; import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { listSqlDatabases } from "../../Utils/arm/generatedClients/cosmos/sqlResources"; import { listSqlDatabases } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
export async function readDatabases(): Promise<DataModels.Database[]> { export async function readDatabases(): Promise<DataModels.Database[]> {
let databases: DataModels.Database[]; let databases: DataModels.Database[];
const clearMessage = logConsoleProgress(`Querying databases`); 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 { try {
if ( if (
userContext.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&

View File

@@ -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, "");
}

View File

@@ -1,19 +1,16 @@
import { CassandraProxyEndpoints, JunoEndpoints, MongoProxyEndpoints } from "Common/Constants";
import { import {
allowedAadEndpoints, allowedAadEndpoints,
allowedArcadiaEndpoints, allowedArcadiaEndpoints,
allowedCassandraProxyEndpoints,
allowedEmulatorEndpoints, allowedEmulatorEndpoints,
allowedGraphEndpoints, allowedGraphEndpoints,
allowedHostedExplorerEndpoints, allowedHostedExplorerEndpoints,
allowedJunoOrigins, allowedJunoOrigins,
allowedMongoBackendEndpoints, allowedMongoBackendEndpoints,
allowedMongoProxyEndpoints,
allowedMsalRedirectEndpoints, allowedMsalRedirectEndpoints,
defaultAllowedArmEndpoints, defaultAllowedArmEndpoints,
defaultAllowedBackendEndpoints, defaultAllowedBackendEndpoints,
validateEndpoint, validateEndpoint,
} from "Utils/EndpointUtils"; } from "Utils/EndpointValidation";
export enum Platform { export enum Platform {
Portal = "Portal", Portal = "Portal",
@@ -40,10 +37,6 @@ export interface ConfigContext {
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string; ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
BACKEND_ENDPOINT?: string; BACKEND_ENDPOINT?: string;
MONGO_BACKEND_ENDPOINT?: string; MONGO_BACKEND_ENDPOINT?: string;
MONGO_PROXY_ENDPOINT?: string;
MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED?: boolean;
NEW_MONGO_APIS?: string[];
CASSANDRA_PROXY_ENDPOINT?: string;
PROXY_PATH?: string; PROXY_PATH?: string;
JUNO_ENDPOINT: string; JUNO_ENDPOINT: string;
GITHUB_CLIENT_ID: string; GITHUB_CLIENT_ID: string;
@@ -71,8 +64,6 @@ let configContext: Readonly<ConfigContext> = {
`^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`, `^https:\\/\\/.*\\.fabric\\.microsoft\\.com$`,
`^https:\\/\\/.*\\.powerbi\\.com$`, `^https:\\/\\/.*\\.powerbi\\.com$`,
`^https:\\/\\/.*\\.analysis-df\\.net$`, `^https:\\/\\/.*\\.analysis-df\\.net$`,
`^https:\\/\\/.*\\.analysis-df\\.windows\\.net$`,
`^https:\\/\\/.*\\.azure-test\\.net$`,
], // Webpack injects this at build time ], // Webpack injects this at build time
gitSha: process.env.GIT_SHA, gitSha: process.env.GIT_SHA,
hostedExplorerURL: "https://cosmos.azure.com/", hostedExplorerURL: "https://cosmos.azure.com/",
@@ -86,19 +77,8 @@ let configContext: Readonly<ConfigContext> = {
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306 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 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", BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
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,
isTerminalEnabled: false, isTerminalEnabled: false,
isPhoenixEnabled: false, isPhoenixEnabled: false,
}; };
@@ -144,18 +124,10 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
delete newContext.BACKEND_ENDPOINT; delete newContext.BACKEND_ENDPOINT;
} }
if (!validateEndpoint(newContext.MONGO_PROXY_ENDPOINT, allowedMongoProxyEndpoints)) {
delete newContext.MONGO_PROXY_ENDPOINT;
}
if (!validateEndpoint(newContext.MONGO_BACKEND_ENDPOINT, allowedMongoBackendEndpoints)) { if (!validateEndpoint(newContext.MONGO_BACKEND_ENDPOINT, allowedMongoBackendEndpoints)) {
delete newContext.MONGO_BACKEND_ENDPOINT; delete newContext.MONGO_BACKEND_ENDPOINT;
} }
if (!validateEndpoint(newContext.CASSANDRA_PROXY_ENDPOINT, allowedCassandraProxyEndpoints)) {
delete newContext.CASSANDRA_PROXY_ENDPOINT;
}
if (!validateEndpoint(newContext.JUNO_ENDPOINT, allowedJunoOrigins)) { if (!validateEndpoint(newContext.JUNO_ENDPOINT, allowedJunoOrigins)) {
delete newContext.JUNO_ENDPOINT; delete newContext.JUNO_ENDPOINT;
} }
@@ -173,7 +145,10 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
// Injected for local development. These will be removed in the production bundle by webpack // Injected for local development. These will be removed in the production bundle by webpack
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
const port: string = process.env.PORT || "1234";
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://localhost:" + port,
MONGO_BACKEND_ENDPOINT: "https://localhost:" + port,
PROXY_PATH: "/proxy", PROXY_PATH: "/proxy",
EMULATOR_ENDPOINT: "https://localhost:8081", EMULATOR_ENDPOINT: "https://localhost:8081",
}); });

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -88,13 +88,13 @@ export interface GenerateTokenResponse {
} }
export interface Subscription { export interface Subscription {
uniqueDisplayName?: string; uniqueDisplayName: string;
displayName: string; displayName: string;
subscriptionId: string; subscriptionId: string;
tenantId?: string; tenantId: string;
state: string; state: string;
subscriptionPolicies?: SubscriptionPolicies; subscriptionPolicies: SubscriptionPolicies;
authorizationSource?: string; authorizationSource: string;
} }
export interface SubscriptionPolicies { export interface SubscriptionPolicies {
@@ -457,11 +457,8 @@ export interface ContainerInfo {
} }
export interface IProvisionData { export interface IProvisionData {
cosmosEndpoint?: string; cosmosEndpoint: string;
poolId: string; poolId: string;
databaseId?: string;
containerId?: string;
mode?: string;
} }
export interface IContainerData { export interface IContainerData {
@@ -604,14 +601,3 @@ export enum PhoenixErrorType {
PhoenixFlightFallback = "PhoenixFlightFallback", PhoenixFlightFallback = "PhoenixFlightFallback",
UserMissingPermissionsError = "UserMissingPermissionsError", UserMissingPermissionsError = "UserMissingPermissionsError",
} }
export interface CopilotEnabledConfiguration {
isEnabled: boolean;
}
export interface FeatureRegistration {
name: string;
properties: {
state: string;
};
}

View File

@@ -1,6 +1,46 @@
import * as ActionContracts from "./ActionContracts"; import * as ActionContracts from "./ActionContracts";
import * as Diagnostics from "./Diagnostics"; import * as Diagnostics from "./Diagnostics";
import { MessageTypes } from "./MessageTypes";
import * as Versions from "./Versions"; import * as Versions from "./Versions";
export { ActionContracts, Diagnostics, MessageTypes, Versions }; /**
* Messaging types used with Data Explorer <-> Portal communication
* and Hosted <-> Explorer communication
*/
export enum MessageTypes {
TelemetryInfo,
LogInfo,
RefreshResources,
AllDatabases,
CollectionsForDatabase,
RefreshOffers,
AllOffers,
UpdateLocationHash,
SingleOffer,
RefreshOffer,
UpdateAccountName,
ForbiddenError,
AadSignIn,
GetAccessAadRequest,
GetAccessAadResponse,
UpdateAccountSwitch,
UpdateDirectoryControl,
SwitchAccount,
SendNotification,
ClearNotification,
ExplorerClickEvent,
LoadingStatus,
GetArcadiaToken,
CreateWorkspace,
CreateSparkPool,
RefreshDatabaseAccount,
CloseTab,
OpenQuickstartBlade,
OpenPostgreSQLPasswordReset,
OpenPostgresNetworkingBlade,
OpenCosmosDBNetworkingBlade,
DisplayNPSSurvey,
OpenVCoreMongoNetworkingBlade,
OpenVCoreMongoConnectionStringsBlade,
}
export { ActionContracts, Diagnostics, Versions };

View File

@@ -0,0 +1,25 @@
export type FabricMessage =
| {
type: "newContainer";
databaseName: string;
}
| {
type: "initialize";
connectionString: string | undefined;
}
| {
type: "openTab";
databaseName: string;
collectionName: string | undefined;
};
export type DataExploreMessage =
| "ready"
| {
type: number;
data: {
action: "LoadDatabases";
actionModifier: "success" | "start";
defaultExperience: "SQL";
};
};

View File

@@ -1,94 +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;
};
}
| {
type: "authorizationToken";
message: {
id: string;
error: string | undefined;
data: AuthorizationToken | undefined;
};
}
| {
type: "allResourceTokens_v2";
message: {
id: string;
error: string | undefined;
data: FabricDatabaseConnectionInfo | undefined;
};
}
| {
type: "setToolbarStatus";
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;
}

View File

@@ -1,55 +0,0 @@
/**
* 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,
LogInfo,
RefreshResources,
AllDatabases,
CollectionsForDatabase,
RefreshOffers,
AllOffers,
UpdateLocationHash,
SingleOffer,
RefreshOffer,
UpdateAccountName,
ForbiddenError,
AadSignIn,
GetAccessAadRequest,
GetAccessAadResponse,
UpdateAccountSwitch,
UpdateDirectoryControl,
SwitchAccount,
SendNotification,
ClearNotification,
ExplorerClickEvent,
LoadingStatus,
GetArcadiaToken,
CreateWorkspace,
CreateSparkPool,
RefreshDatabaseAccount,
CloseTab,
OpenQuickstartBlade,
OpenPostgreSQLPasswordReset,
OpenPostgresNetworkingBlade,
OpenCosmosDBNetworkingBlade,
DisplayNPSSurvey,
OpenVCoreMongoNetworkingBlade,
OpenVCoreMongoConnectionStringsBlade,
GetAuthorizationToken, // Data Explorer -> Fabric
GetAllResourceTokens, // Data Explorer -> Fabric
Ready, // Data Explorer -> Fabric
OpenCESCVAFeedbackBlade,
}
export interface AuthorizationToken {
XDate: string;
PrimaryReadWriteToken: string;
}

View File

@@ -386,10 +386,9 @@ export interface DataExplorerInputsFrame {
dnsSuffix?: string; dnsSuffix?: string;
serverId?: string; serverId?: string;
extensionEndpoint?: string; extensionEndpoint?: string;
mongoProxyEndpoint?: string;
cassandraProxyEndpoint?: string;
subscriptionType?: SubscriptionType; subscriptionType?: SubscriptionType;
quotaId?: string; quotaId?: string;
addCollectionDefaultFlight?: string;
isTryCosmosDBSubscription?: boolean; isTryCosmosDBSubscription?: boolean;
loadDatabaseAccountTimestamp?: number; loadDatabaseAccountTimestamp?: number;
sharedThroughputMinimum?: number; sharedThroughputMinimum?: number;

View File

@@ -109,7 +109,6 @@ describe("iframe rendering when there is no data", () => {
theme: 4, theme: 4,
}, },
}, },
origin: "http://localhost",
}; };
const divElement = `<div id="${Heatmap.elementId}"></div>`; const divElement = `<div id="${Heatmap.elementId}"></div>`;
@@ -130,7 +129,6 @@ describe("iframe rendering when there is no data", () => {
theme: 2, theme: 2,
}, },
}, },
origin: "http://localhost",
}; };
const divElement = `<div id="${Heatmap.elementId}"></div>`; const divElement = `<div id="${Heatmap.elementId}"></div>`;

1954
src/Definitions/datatables.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
* https://github.com/running-coder/jquery-typeahead/issues/156 * https://github.com/running-coder/jquery-typeahead/issues/156
* TODO: Replace this minimum definition by the official one when it comes out. * TODO: Replace this minimum definition by the official one when it comes out.
*/ */
/// <reference types="jquery" /> /// <reference path="jquery.d.ts" />
interface JQueryTypeaheadParam { interface JQueryTypeaheadParam {
input: string; input: string;

View File

@@ -3,7 +3,7 @@
// Definitions by: Boris Yankov <https://github.com/borisyankov/>, John Reilly <https://github.com/johnnyreilly> // Definitions by: Boris Yankov <https://github.com/borisyankov/>, John Reilly <https://github.com/johnnyreilly>
// Definitions: https://github.com/borisyankov/DefinitelyTyped // Definitions: https://github.com/borisyankov/DefinitelyTyped
/// <reference types="jquery"/> /// <reference path="jquery.d.ts"/>
declare namespace JQueryUI { declare namespace JQueryUI {
// Accordion ////////////////////////////////////////////////// // Accordion //////////////////////////////////////////////////

1890
src/Definitions/jquery.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +0,0 @@
declare module "*.less" {
const value: string;
export default value;
}

View File

@@ -129,22 +129,20 @@ export const createCollectionContextMenuButton = (
}); });
} }
if (configContext.platform !== Platform.Fabric) { items.push({
items.push({ iconSrc: DeleteCollectionIcon,
iconSrc: DeleteCollectionIcon, onClick: () => {
onClick: () => { useSelectedNode.getState().setSelectedNode(selectedCollection);
useSelectedNode.getState().setSelectedNode(selectedCollection); useSidePanel
useSidePanel .getState()
.getState() .openSidePanel(
.openSidePanel( "Delete " + getCollectionName(),
"Delete " + getCollectionName(), <DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />, );
); },
}, label: `Delete ${getCollectionName()}`,
label: `Delete ${getCollectionName()}`, styleClass: "deleteCollectionMenuItem",
styleClass: "deleteCollectionMenuItem", });
});
}
return items; return items;
}; };

View File

@@ -27,7 +27,6 @@ export interface AccordionItemComponentProps {
isExpanded?: boolean; isExpanded?: boolean;
containerStyles?: React.CSSProperties; containerStyles?: React.CSSProperties;
styles?: React.CSSProperties; styles?: React.CSSProperties;
children: JSX.Element;
} }
interface AccordionItemComponentState { interface AccordionItemComponentState {

View File

@@ -16,7 +16,6 @@ export interface CollapsiblePanelProps {
isCollapsed: boolean; isCollapsed: boolean;
onCollapsedChanged: (newValue: boolean) => void; onCollapsedChanged: (newValue: boolean) => void;
collapseToLeft?: boolean; collapseToLeft?: boolean;
children: JSX.Element | JSX.Element[];
} }
export class CollapsiblePanel extends React.Component<CollapsiblePanelProps> { export class CollapsiblePanel extends React.Component<CollapsiblePanelProps> {

View File

@@ -7,7 +7,6 @@ describe("CollapsibleSectionComponent", () => {
const props: CollapsibleSectionProps = { const props: CollapsibleSectionProps = {
title: "Sample title", title: "Sample title",
isExpandedByDefault: true, isExpandedByDefault: true,
children: <></>,
}; };
const wrapper = shallow(<CollapsibleSectionComponent {...props} />); const wrapper = shallow(<CollapsibleSectionComponent {...props} />);

View File

@@ -7,7 +7,6 @@ export interface CollapsibleSectionProps {
title: string; title: string;
isExpandedByDefault: boolean; isExpandedByDefault: boolean;
onExpand?: () => void; onExpand?: () => void;
children: JSX.Element;
} }
export interface CollapsibleSectionState { export interface CollapsibleSectionState {

View File

@@ -99,7 +99,7 @@ export class DiffEditorViewModel {
) { ) {
this.editorContainer = document.getElementById(this.getEditorId()); this.editorContainer = document.getElementById(this.getEditorId());
this.editorContainer.innerHTML = ""; this.editorContainer.innerHTML = "";
const options: monaco.editor.IStandaloneDiffEditorConstructionOptions = { const options: monaco.editor.IDiffEditorConstructionOptions = {
lineNumbers: this.params.lineNumbers || "off", lineNumbers: this.params.lineNumbers || "off",
fontSize: 12, fontSize: 12,
ariaLabel: this.params.ariaLabel, ariaLabel: this.params.ariaLabel,

View File

@@ -52,11 +52,7 @@ class EditorViewModel extends JsonEditorViewModel {
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) { if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service"); const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service");
const monaco = await loadMonaco(); const monaco = await loadMonaco();
monaco.languages.registerCompletionItemProvider( monaco.languages.registerCompletionItemProvider("sql", new SqlCompletionItemProvider());
"sql",
// TODO cosmos-language-service could be outdated
new SqlCompletionItemProvider() as unknown as monaco.languages.CompletionItemProvider,
);
EditorViewModel.providerRegistered.push("sql"); EditorViewModel.providerRegistered.push("sql");
} }
} }

View File

@@ -48,7 +48,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
public componentDidUpdate(previous: EditorReactProps) { public componentDidUpdate(previous: EditorReactProps) {
if (this.props.content !== previous.content) { 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 * Create the monaco editor and attach to DOM
*/ */
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) { private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
const options: monaco.editor.IStandaloneEditorConstructionOptions = { const options: monaco.editor.IEditorConstructionOptions = {
language: this.props.language, language: this.props.language,
value: this.props.content, value: this.props.content,
readOnly: this.props.isReadOnly, readOnly: this.props.isReadOnly,
@@ -111,7 +111,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
this.rootNode.innerHTML = ""; this.rootNode.innerHTML = "";
const monaco = await loadMonaco(); const monaco = await loadMonaco();
createCallback(monaco?.editor?.create(this.rootNode, options)); createCallback(monaco.editor.create(this.rootNode, options));
if (this.rootNode.innerHTML) { if (this.rootNode.innerHTML) {
this.setState({ this.setState({

View File

@@ -90,7 +90,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
protected async createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) { protected async createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
this.registerCompletionItemProvider(); this.registerCompletionItemProvider();
this.editorContainer = document.getElementById(this.getEditorId()); this.editorContainer = document.getElementById(this.getEditorId());
const options: monaco.editor.IStandaloneEditorConstructionOptions = { const options: monaco.editor.IEditorConstructionOptions = {
value: content, value: content,
language: this.getEditorLanguage(), language: this.getEditorLanguage(),
readOnly: this.params.isReadOnly, readOnly: this.params.isReadOnly,

View File

@@ -29,6 +29,6 @@ describe("CodeOfConduct", () => {
const wrapper = shallow(<CodeOfConduct {...codeOfConductProps} />); const wrapper = shallow(<CodeOfConduct {...codeOfConductProps} />);
wrapper.find(".genericPaneSubmitBtn").first().simulate("click"); wrapper.find(".genericPaneSubmitBtn").first().simulate("click");
await Promise.resolve(); await Promise.resolve();
expect(codeOfConductProps.onAcceptCodeOfConduct).toHaveBeenCalled(); expect(codeOfConductProps.onAcceptCodeOfConduct).toBeCalled();
}); });
}); });

View File

@@ -9,7 +9,7 @@ import { updateUserContext } from "../../../UserContext";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";
import { CollectionSettingsTabV2 } from "../../Tabs/SettingsTabV2"; import { CollectionSettingsTabV2 } from "../../Tabs/SettingsTabV2";
import { SettingsComponent, SettingsComponentProps, SettingsComponentState } from "./SettingsComponent"; import { SettingsComponent, SettingsComponentProps, SettingsComponentState } from "./SettingsComponent";
import { TtlType, isDirty } from "./SettingsUtils"; import { isDirty, TtlType } from "./SettingsUtils";
import { collection } from "./TestUtils"; import { collection } from "./TestUtils";
jest.mock("../../../Common/dataAccess/getIndexTransformationProgress", () => ({ jest.mock("../../../Common/dataAccess/getIndexTransformationProgress", () => ({
getIndexTransformationProgress: jest.fn().mockReturnValue(undefined), getIndexTransformationProgress: jest.fn().mockReturnValue(undefined),
@@ -190,8 +190,8 @@ describe("SettingsComponent", () => {
id: "id", id: "id",
}; };
await settingsComponentInstance.onSaveClick(); await settingsComponentInstance.onSaveClick();
expect(updateCollection).toHaveBeenCalled(); expect(updateCollection).toBeCalled();
expect(updateOffer).toHaveBeenCalled(); expect(updateOffer).toBeCalled();
}); });
it("revert resets state values", async () => { it("revert resets state values", async () => {

View File

@@ -1,6 +1,5 @@
import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "@fluentui/react"; import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "@fluentui/react";
import { useDatabases } from "Explorer/useDatabases"; import { useDatabases } from "Explorer/useDatabases";
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
import * as React from "react"; import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg"; import DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg"; import SaveIcon from "../../../../images/save-cosmos.svg";
@@ -19,10 +18,6 @@ import { userContext } from "../../../UserContext";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types"; import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import {
PartitionKeyComponent,
PartitionKeyComponentProps,
} from "../../Controls/Settings/SettingsSubComponents/PartitionKeyComponent";
import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter";
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2"; import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
import "./SettingsComponent.less"; import "./SettingsComponent.less";
@@ -133,7 +128,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private changeFeedPolicyVisible: boolean; private changeFeedPolicyVisible: boolean;
private isFixedContainer: boolean; private isFixedContainer: boolean;
private shouldShowIndexingPolicyEditor: boolean; private shouldShowIndexingPolicyEditor: boolean;
private shouldShowPartitionKeyEditor: boolean;
private totalThroughputUsed: number; private totalThroughputUsed: number;
public mongoDBCollectionResource: MongoDBCollectionResource; public mongoDBCollectionResource: MongoDBCollectionResource;
@@ -146,7 +140,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.offer = this.collection?.offer(); this.offer = this.collection?.offer();
this.isAnalyticalStorageEnabled = !!this.collection?.analyticalStorageTtl(); this.isAnalyticalStorageEnabled = !!this.collection?.analyticalStorageTtl();
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo"; this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
this.shouldShowPartitionKeyEditor = userContext.apiType === "SQL" && isRunningOnPublicCloud();
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy; this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
@@ -1063,12 +1056,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
onConflictResolutionDirtyChange: this.onConflictResolutionDirtyChange, onConflictResolutionDirtyChange: this.onConflictResolutionDirtyChange,
}; };
const partitionKeyComponentProps: PartitionKeyComponentProps = {
database: useDatabases.getState().findDatabaseWithId(this.collection.databaseId),
collection: this.collection,
explorer: this.props.settingsTab.getContainer(),
};
const tabs: SettingsV2TabInfo[] = []; const tabs: SettingsV2TabInfo[] = [];
if (!hasDatabaseSharedThroughput(this.collection) && this.offer) { if (!hasDatabaseSharedThroughput(this.collection) && this.offer) {
tabs.push({ tabs.push({
@@ -1104,13 +1091,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
}); });
} }
if (this.shouldShowPartitionKeyEditor) {
tabs.push({
tab: SettingsV2TabTypes.PartitionKeyTab,
content: <PartitionKeyComponent {...partitionKeyComponentProps} />,
});
}
const pivotProps: IPivotProps = { const pivotProps: IPivotProps = {
onLinkClick: this.onPivotChange, onLinkClick: this.onPivotChange,
selectedKey: SettingsV2TabTypes[this.state.selectedTab], selectedKey: SettingsV2TabTypes[this.state.selectedTab],

View File

@@ -1,8 +1,8 @@
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import React from "react"; import React from "react";
import { renderToString } from "react-dom/server";
import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils"; import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils";
import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent"; import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent";
import { renderToString } from "react-dom/server";
describe("MongoIndexingPolicyComponent", () => { describe("MongoIndexingPolicyComponent", () => {
const baseProps: MongoIndexingPolicyComponentProps = { const baseProps: MongoIndexingPolicyComponentProps = {
@@ -84,7 +84,7 @@ describe("MongoIndexingPolicyComponent", () => {
]; ];
test.each(cases)( test.each(cases)(
"mongo indexing policy saveable and discardable", "",
( (
notification: MongoNotificationMessage, notification: MongoNotificationMessage,
indexToDropIsPresent: boolean, indexToDropIsPresent: boolean,
@@ -111,10 +111,8 @@ describe("MongoIndexingPolicyComponent", () => {
); );
if (mongoWarningNotificationMessage) { if (mongoWarningNotificationMessage) {
const elementAsString = renderToString(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()); const elementAsString = renderToString(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage());
// eslint-disable-next-line jest/no-conditional-expect
expect(elementAsString).toContain(mongoWarningNotificationMessage); expect(elementAsString).toContain(mongoWarningNotificationMessage);
} else { } else {
// eslint-disable-next-line jest/no-conditional-expect
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toBeUndefined(); expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toBeUndefined();
} }
}, },

View File

@@ -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 processedCount = portalDataTransferJob?.properties?.processedCount;
const totalCount = portalDataTransferJob?.properties?.totalCount;
const jobStatus = portalDataTransferJob?.properties?.status;
const isCancelled = jobStatus === "Cancelled";
const isCompleted = jobStatus === "Completed";
if (totalCount <= 0 && !isCompleted) {
return isCancelled ? 0 : null;
}
return isCompleted ? 1 : processedCount / totalCount;
};
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>
);
};

View File

@@ -306,7 +306,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
}; };
const costElement = (): JSX.Element => { const costElement = (): JSX.Element => {
const prices: PriceBreakdown = getRuPriceBreakdown(throughput, serverId, numberOfRegions, isMultimaster, false); const prices: PriceBreakdown = getRuPriceBreakdown(throughput, serverId, numberOfRegions, isMultimaster, true);
return ( return (
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}> <Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
{newThroughput && newThroughputCostElement()} {newThroughput && newThroughputCostElement()}

View File

@@ -917,7 +917,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
> >
$ $
0.0080 0.012
/hr /hr
</Text> </Text>
<Text <Text
@@ -929,7 +929,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
> >
$ $
0.19 0.29
/day /day
</Text> </Text>
<Text <Text
@@ -941,7 +941,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
> >
$ $
5.84 8.76
/mo /mo
</Text> </Text>
</Stack> </Stack>
@@ -1354,7 +1354,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
> >
$ $
0.0080 0.012
/hr /hr
</Text> </Text>
<Text <Text
@@ -1366,7 +1366,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
> >
$ $
0.19 0.29
/day /day
</Text> </Text>
<Text <Text
@@ -1378,7 +1378,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
> >
$ $
5.84 8.76
/mo /mo
</Text> </Text>
</Stack> </Stack>

View File

@@ -45,7 +45,6 @@ export enum SettingsV2TabTypes {
ConflictResolutionTab, ConflictResolutionTab,
SubSettingsTab, SubSettingsTab,
IndexingPolicyTab, IndexingPolicyTab,
PartitionKeyTab,
} }
export interface IsComponentDirtyResult { export interface IsComponentDirtyResult {
@@ -147,8 +146,6 @@ export const getTabTitle = (tab: SettingsV2TabTypes): string => {
return "Settings"; return "Settings";
case SettingsV2TabTypes.IndexingPolicyTab: case SettingsV2TabTypes.IndexingPolicyTab:
return "Indexing Policy"; return "Indexing Policy";
case SettingsV2TabTypes.PartitionKeyTab:
return "Partition Keys";
default: default:
throw new Error(`Unknown tab ${tab}`); throw new Error(`Unknown tab ${tab}`);
} }
@@ -202,49 +199,3 @@ export const getMongoIndexTypeText = (index: MongoIndexTypes): string => {
export const isIndexTransforming = (indexTransformationProgress: number): boolean => export const isIndexTransforming = (indexTransformationProgress: number): boolean =>
// index transformation progress can be 0 // index transformation progress can be 0
indexTransformationProgress !== undefined && indexTransformationProgress !== 100; indexTransformationProgress !== undefined && indexTransformationProgress !== 100;
export const getPartitionKeyName = (apiType: string, isLowerCase?: boolean): string => {
const partitionKeyName = apiType === "Mongo" ? "Shard key" : "Partition key";
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
};
export const getPartitionKeyTooltipText = (apiType: string): string => {
if (apiType === "Mongo") {
return "The shard key (field) is used to split your data across many replica sets (shards) to achieve unlimited scalability. Its critical to choose a field that will evenly distribute your data.";
}
let tooltipText = `The ${getPartitionKeyName(
apiType,
true,
)} is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume.`;
if (apiType === "SQL") {
tooltipText += " For small read-heavy workloads or write-heavy workloads of any size, id is often a good choice.";
}
return tooltipText;
};
export const getPartitionKeySubtext = (partitionKeyDefault: boolean, apiType: string): string => {
if (partitionKeyDefault && (apiType === "SQL" || apiType === "Mongo")) {
const subtext = "For small workloads, the item ID is a suitable choice for the partition key.";
return subtext;
}
return "";
};
export const getPartitionKeyPlaceHolder = (apiType: string, index?: number): string => {
switch (apiType) {
case "Mongo":
return "e.g., categoryId";
case "Gremlin":
return "e.g., /address";
case "SQL":
return `${
index === undefined
? "Required - first partition key e.g., /TenantId"
: index === 0
? "second partition key e.g., /UserId"
: "third partition key e.g., /SessionId"
}`;
default:
return "e.g., /address/zipCode";
}
};

View File

@@ -204,98 +204,6 @@ exports[`SettingsComponent renders 1`] = `
shouldDiscardIndexingPolicy={false} shouldDiscardIndexingPolicy={false}
/> />
</PivotItem> </PivotItem>
<PivotItem
headerText="Partition Keys"
itemKey="PartitionKeyTab"
key="PartitionKeyTab"
style={
Object {
"marginTop": 20,
}
}
>
<PartitionKeyComponent
collection={
Object {
"analyticalStorageTtl": [Function],
"changeFeedPolicy": [Function],
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
"retries": 3,
},
},
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
},
"refreshNotebookList": [Function],
"resourceTree": ResourceTreeAdapter {
"container": [Circular],
"copyNotebook": [Function],
"parameters": [Function],
},
},
"databaseId": "test",
"defaultTtl": [Function],
"geospatialConfig": [Function],
"getDatabase": [Function],
"id": [Function],
"indexingPolicy": [Function],
"offer": [Function],
"partitionKey": Object {
"kind": "hash",
"paths": Array [],
"version": 2,
},
"partitionKeyProperties": Array [
"partitionKey",
],
"readSettings": [Function],
"uniqueKeyPolicy": Object {},
"usageSizeInKB": [Function],
}
}
explorer={
Explorer {
"_isInitializingNotebooks": false,
"_resetNotebookWorkspace": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
"retries": 3,
},
},
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
"container": [Circular],
},
"refreshNotebookList": [Function],
"resourceTree": ResourceTreeAdapter {
"container": [Circular],
"copyNotebook": [Function],
"parameters": [Function],
},
}
}
/>
</PivotItem>
</StyledPivot> </StyledPivot>
</div> </div>
</div> </div>

View File

@@ -23,12 +23,12 @@ describe("ThroughputInput Pane", () => {
}); });
it("should switch mode properly", () => { it("should switch mode properly", () => {
wrapper.find('[id="Manual-input"]').simulate("change"); wrapper.find('[aria-label="Manual database throughput"]').simulate("change");
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe( expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe(
"Container throughput (400 - unlimited RU/s)", "Container throughput (400 - unlimited RU/s)",
); );
wrapper.find('[id="Autoscale-input"]').simulate("change"); wrapper.find('[aria-label="Autoscale database throughput"]').simulate("change");
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)"); expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)");
}); });
}); });

View File

@@ -189,7 +189,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<input <input
id="Autoscale-input" id="Autoscale-input"
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
aria-label={`${getThroughputLabelText()} Autoscale`} aria-label="Autoscale database throughput"
aria-required={true} aria-required={true}
checked={isAutoscaleSelected} checked={isAutoscaleSelected}
type="radio" type="radio"
@@ -204,7 +204,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<input <input
id="Manual-input" id="Manual-input"
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
aria-label={`${getThroughputLabelText()} Manual`} aria-label="Manual database throughput"
checked={!isAutoscaleSelected} checked={!isAutoscaleSelected}
type="radio" type="radio"
aria-required={true} aria-required={true}
@@ -271,17 +271,11 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<Stack className="throughputInputSpacing"> <Stack className="throughputInputSpacing">
<Text variant="small" aria-label="ruDescription"> <Text variant="small" aria-label="ruDescription">
Estimate your required RU/s with&nbsp; Estimate your required RU/s with&nbsp;
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="Capacity calculator"> <Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="capacityLink">
capacity calculator capacity calculator
</Link> </Link>
. .
</Text> </Text>
<Stack horizontal>
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }} aria-label="maxRUDescription">
{isDatabase ? "Database" : getCollectionName()} Required RU/s
</Text>
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
</Stack>
<TooltipHost <TooltipHost
directionalHint={DirectionalHint.topLeftEdge} directionalHint={DirectionalHint.topLeftEdge}
@@ -302,7 +296,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
min={SharedConstants.CollectionCreation.DefaultCollectionRUs400} min={SharedConstants.CollectionCreation.DefaultCollectionRUs400}
max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity} max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity}
value={throughput.toString()} value={throughput.toString()}
ariaLabel={`${isDatabase ? "Database" : getCollectionName()} Required RU/s`} aria-label="Max request units per second"
required={true} required={true}
errorMessage={throughputError} errorMessage={throughputError}
/> />

View File

@@ -18,17 +18,17 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
horizontal={true} horizontal={true}
> >
<div <div
className="ms-Stack css-109" className="ms-Stack css-53"
> >
<span <span
className="mandatoryStar" className="mandatoryStar"
key=".0:$.$.0" key=".0:$.0"
> >
*  * 
</span> </span>
<Text <Text
aria-label="Throughput header" aria-label="Throughput header"
key=".0:$.$.1" key=".0:$.1"
style={ style={
Object { Object {
"fontWeight": 600, "fontWeight": 600,
@@ -39,7 +39,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
> >
<span <span
aria-label="Throughput header" aria-label="Throughput header"
className="css-110" className="css-54"
style={ style={
Object { Object {
"fontWeight": 600, "fontWeight": 600,
@@ -51,7 +51,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</span> </span>
</Text> </Text>
<InfoTooltip <InfoTooltip
key=".0:$.$.2" key=".0:$.2"
> >
<span> <span>
<StyledTooltipHostBase <StyledTooltipHostBase
@@ -336,13 +336,12 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
} }
> >
<div <div
className="ms-TooltipHost root-111" className="ms-TooltipHost root-55"
onBlurCapture={[Function]} onBlurCapture={[Function]}
onFocusCapture={[Function]} onFocusCapture={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
onMouseEnter={[Function]} onMouseEnter={[Function]}
onMouseLeave={[Function]} onMouseLeave={[Function]}
role="none"
> >
<StyledIconBase <StyledIconBase
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage." ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
@@ -632,7 +631,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
> >
<i <i
aria-label="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage." aria-label="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
className="panelInfoIcon root-114" className="panelInfoIcon root-57"
data-icon-name="Info" data-icon-name="Info"
role="img" role="img"
tabIndex={0} tabIndex={0}
@@ -641,24 +640,6 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</i> </i>
</IconBase> </IconBase>
</StyledIconBase> </StyledIconBase>
<div
hidden={true}
id="tooltip0"
style={
Object {
"border": 0,
"height": 1,
"margin": -1,
"overflow": "hidden",
"padding": 0,
"position": "absolute",
"whiteSpace": "nowrap",
"width": 1,
}
}
>
Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage.
</div>
</div> </div>
</TooltipHostBase> </TooltipHostBase>
</StyledTooltipHostBase> </StyledTooltipHostBase>
@@ -671,14 +652,14 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
verticalAlign="center" verticalAlign="center"
> >
<div <div
className="ms-Stack css-115" className="ms-Stack css-58"
> >
<div <div
key=".0:$.$.0" key=".0:$.0"
role="radiogroup" role="radiogroup"
> >
<input <input
aria-label="Container throughput (autoscale) Autoscale" aria-label="Autoscale database throughput"
aria-required={true} aria-required={true}
checked={true} checked={true}
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
@@ -695,7 +676,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
Autoscale Autoscale
</label> </label>
<input <input
aria-label="Container throughput (autoscale) Manual" aria-label="Manual database throughput"
aria-required={true} aria-required={true}
checked={false} checked={false}
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
@@ -718,16 +699,16 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
className="throughputInputSpacing" className="throughputInputSpacing"
> >
<div <div
className="ms-Stack throughputInputSpacing css-116" className="ms-Stack throughputInputSpacing css-59"
> >
<Text <Text
aria-label="capacity calculator of azure cosmos db" aria-label="capacity calculator of azure cosmos db"
key=".0:$.$.0" key=".0:$.0"
variant="small" variant="small"
> >
<span <span
aria-label="capacity calculator of azure cosmos db" aria-label="capacity calculator of azure cosmos db"
className="css-110" className="css-54"
> >
Estimate your required RU/s with Estimate your required RU/s with
@@ -1017,7 +998,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
> >
<a <a
aria-label="capacity calculator of azure cosmos db" aria-label="capacity calculator of azure cosmos db"
className="ms-Link root-117" className="ms-Link root-60"
href="https://cosmos.azure.com/capacitycalculator/" href="https://cosmos.azure.com/capacitycalculator/"
onClick={[Function]} onClick={[Function]}
target="_blank" target="_blank"
@@ -1031,14 +1012,14 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</Text> </Text>
<Stack <Stack
horizontal={true} horizontal={true}
key=".0:$.$.1" key=".0:$.1"
> >
<div <div
className="ms-Stack css-109" className="ms-Stack css-53"
> >
<Text <Text
aria-label="maxRUDescription" aria-label="maxRUDescription"
key=".0:$.$.0" key=".0:$.0"
style={ style={
Object { Object {
"fontWeight": 600, "fontWeight": 600,
@@ -1049,7 +1030,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
> >
<span <span
aria-label="maxRUDescription" aria-label="maxRUDescription"
className="css-110" className="css-54"
style={ style={
Object { Object {
"fontWeight": 600, "fontWeight": 600,
@@ -1062,7 +1043,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</span> </span>
</Text> </Text>
<InfoTooltip <InfoTooltip
key=".0:$.$.1" key=".0:$.1"
> >
<span> <span>
<StyledTooltipHostBase <StyledTooltipHostBase
@@ -1347,13 +1328,12 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
} }
> >
<div <div
className="ms-TooltipHost root-111" className="ms-TooltipHost root-55"
onBlurCapture={[Function]} onBlurCapture={[Function]}
onFocusCapture={[Function]} onFocusCapture={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
onMouseEnter={[Function]} onMouseEnter={[Function]}
onMouseLeave={[Function]} onMouseLeave={[Function]}
role="none"
> >
<StyledIconBase <StyledIconBase
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage." ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
@@ -1643,7 +1623,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
> >
<i <i
aria-label="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage." aria-label="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
className="panelInfoIcon root-114" className="panelInfoIcon root-57"
data-icon-name="Info" data-icon-name="Info"
role="img" role="img"
tabIndex={0} tabIndex={0}
@@ -1652,24 +1632,6 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</i> </i>
</IconBase> </IconBase>
</StyledIconBase> </StyledIconBase>
<div
hidden={true}
id="tooltip1"
style={
Object {
"border": 0,
"height": 1,
"margin": -1,
"overflow": "hidden",
"padding": 0,
"position": "absolute",
"whiteSpace": "nowrap",
"width": 1,
}
}
>
Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage.
</div>
</div> </div>
</TooltipHostBase> </TooltipHostBase>
</StyledTooltipHostBase> </StyledTooltipHostBase>
@@ -1681,7 +1643,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
ariaLabel="Container max RU/s" ariaLabel="Container max RU/s"
errorMessage="" errorMessage=""
id="autoscaleRUValueField" id="autoscaleRUValueField"
key=".0:$.$.2" key=".0:$.2"
max="9007199254740991" max="9007199254740991"
min={1000} min={1000}
onChange={[Function]} onChange={[Function]}
@@ -1991,18 +1953,18 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
value="4000" value="4000"
> >
<div <div
className="ms-TextField is-required root-119" className="ms-TextField is-required root-62"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<div <div
className="ms-TextField-fieldGroup fieldGroup-120" className="ms-TextField-fieldGroup fieldGroup-63"
> >
<input <input
aria-invalid={false} aria-invalid={false}
aria-label="Container max RU/s" aria-label="Container max RU/s"
className="ms-TextField-field field-121" className="ms-TextField-field field-64"
id="autoscaleRUValueField" id="autoscaleRUValueField"
max="9007199254740991" max="9007199254740991"
min={1000} min={1000}
@@ -2021,11 +1983,11 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
</TextFieldBase> </TextFieldBase>
</StyledTextFieldBase> </StyledTextFieldBase>
<Text <Text
key=".0:$.$.3" key=".0:$.3"
variant="small" variant="small"
> >
<span <span
className="css-110" className="css-54"
> >
Your Your
container container

View File

@@ -59,10 +59,6 @@
} }
} }
[data-test="Gallery"]{
outline-offset: -1px;
}
.selected { .selected {
& > .treeNodeHeader { & > .treeNodeHeader {
background-color: @AccentExtra; background-color: @AccentExtra;

View File

@@ -123,6 +123,19 @@ describe("ContainerSampleGenerator", () => {
await generator.createSampleContainerAsync(); await generator.createSampleContainerAsync();
}); });
it("should not create any sample for Mongo API account", async () => {
const experience = "Sample generation not supported for this API Mongo";
updateUserContext({
databaseAccount: {
properties: {
capabilities: [{ name: "EnableMongo" }],
},
} as DatabaseAccount,
});
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
});
it("should not create any sample for Table API account", async () => { it("should not create any sample for Table API account", async () => {
const experience = "Sample generation not supported for this API Tables"; const experience = "Sample generation not supported for this API Tables";
updateUserContext({ updateUserContext({

View File

@@ -3,11 +3,8 @@ import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { sendMessage } from "Common/MessageHandler"; import { sendMessage } from "Common/MessageHandler";
import { Platform, configContext } from "ConfigContext"; import { Platform, configContext } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts"; import { MessageTypes } from "Contracts/ExplorerContracts";
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { IGalleryItem } from "Juno/JunoClient"; import { IGalleryItem } from "Juno/JunoClient";
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil"; import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout"; import * as ko from "knockout";
import React from "react"; import React from "react";
@@ -94,7 +91,7 @@ export default class Explorer {
}; };
private static readonly MaxNbDatabasesToAutoExpand = 5; private static readonly MaxNbDatabasesToAutoExpand = 5;
public phoenixClient: PhoenixClient; private phoenixClient: PhoenixClient;
constructor() { constructor() {
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, { const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree, dataExplorerArea: Constants.Areas.ResourceTree,
@@ -265,33 +262,58 @@ export default class Explorer {
// TODO: return result // TODO: return result
} }
private getRandomInt(max: number) {
return Math.floor(Math.random() * max);
}
public openNPSSurveyDialog(): void { public openNPSSurveyDialog(): void {
if (!Platform.Portal) { if (!Platform.Portal) {
return; return;
} }
const NINETY_DAYS_IN_MS = 7776000000;
const ONE_DAY_IN_MS = 86400000; const ONE_DAY_IN_MS = 86400000;
const SEVEN_DAYS_IN_MS = 604800000; const isAccountNewerThanNinetyDays = isAccountNewerThanThresholdInMs(
userContext.databaseAccount?.systemData?.createdAt || "",
NINETY_DAYS_IN_MS,
);
const lastSubmitted: string = localStorage.getItem("lastSubmitted");
// Try Cosmos DB subscription - survey shown to 100% of users at day 1 in Data Explorer. if (lastSubmitted !== null) {
let lastSubmittedDate: number = parseInt(lastSubmitted);
if (isNaN(lastSubmittedDate)) {
lastSubmittedDate = 0;
}
const nowMs: number = Date.now();
const millisecsSinceLastSubmitted = nowMs - lastSubmittedDate;
if (millisecsSinceLastSubmitted < NINETY_DAYS_IN_MS) {
return;
}
}
// Try Cosmos DB subscription - survey shown to random 25% of users at day 1 in Data Explorer.
if (userContext.isTryCosmosDBSubscription) { if (userContext.isTryCosmosDBSubscription) {
if (isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS)) { if (
Logger.logInfo( isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS) &&
`Sending message to Portal to check if NPS Survey can be displayed in Try Cosmos DB ${userContext.apiType}`, this.getRandomInt(100) < 25
"Explorer/openNPSSurveyDialog", ) {
);
sendMessage({ type: MessageTypes.DisplayNPSSurvey }); sendMessage({ type: MessageTypes.DisplayNPSSurvey });
localStorage.setItem("lastSubmitted", Date.now().toString());
} }
} else { } else {
// Show survey when an existing account is older than 7 days // An existing account is lesser than 90 days old. For existing account show to random 10 % of users in Data Explorer.
if ( if (isAccountNewerThanNinetyDays) {
!isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", SEVEN_DAYS_IN_MS) if (this.getRandomInt(100) < 10) {
) { sendMessage({ type: MessageTypes.DisplayNPSSurvey });
Logger.logInfo( localStorage.setItem("lastSubmitted", Date.now().toString());
`Sending message to Portal to check if NPS Survey can be displayed for existing ${userContext.apiType} account older than 7 days`, }
"Explorer/openNPSSurveyDialog", } else {
); // An existing account is greater than 90 days. For existing account show to random 25 % of users in Data Explorer.
sendMessage({ type: MessageTypes.DisplayNPSSurvey }); if (this.getRandomInt(100) < 25) {
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
localStorage.setItem("lastSubmitted", Date.now().toString());
}
} }
} }
} }
@@ -357,11 +379,6 @@ export default class Explorer {
}; };
public onRefreshResourcesClick = (): void => { public onRefreshResourcesClick = (): void => {
if (configContext.platform === Platform.Fabric) {
scheduleRefreshDatabaseResourceToken(true).then(() => this.refreshAllDatabases());
return;
}
userContext.authType === AuthType.ResourceToken userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken() ? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases(); : this.refreshAllDatabases();
@@ -386,7 +403,7 @@ export default class Explorer {
this._isInitializingNotebooks = false; this._isInitializingNotebooks = false;
} }
public async allocateContainer(poolId: PoolIdType, mode?: string): Promise<void> { public async allocateContainer(poolId: PoolIdType): Promise<void> {
const shouldUseNotebookStates = poolId === PoolIdType.DefaultPoolId ? true : false; const shouldUseNotebookStates = poolId === PoolIdType.DefaultPoolId ? true : false;
const notebookServerInfo = shouldUseNotebookStates const notebookServerInfo = shouldUseNotebookStates
? useNotebook.getState().notebookServerInfo ? useNotebook.getState().notebookServerInfo
@@ -400,6 +417,10 @@ export default class Explorer {
(notebookServerInfo === undefined || (notebookServerInfo === undefined ||
(notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined)) (notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined))
) { ) {
const provisionData: IProvisionData = {
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
poolId: shouldUseNotebookStates ? undefined : poolId,
};
const connectionStatus: ContainerConnectionInfo = { const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Connecting, status: ConnectionStatusType.Connecting,
}; };
@@ -407,26 +428,14 @@ export default class Explorer {
shouldUseNotebookStates && useNotebook.getState().setConnectionInfo(connectionStatus); shouldUseNotebookStates && useNotebook.getState().setConnectionInfo(connectionStatus);
let connectionInfo; let connectionInfo;
let provisionData: IProvisionData;
try { try {
TelemetryProcessor.traceStart(Action.PhoenixConnection, { TelemetryProcessor.traceStart(Action.PhoenixConnection, {
dataExplorerArea: Areas.Notebook, dataExplorerArea: Areas.Notebook,
}); });
if (shouldUseNotebookStates) { shouldUseNotebookStates
useNotebook.getState().setIsAllocating(true); ? useNotebook.getState().setIsAllocating(true)
provisionData = { : useQueryCopilot.getState().setIsAllocatingContainer(true);
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
poolId: undefined,
};
} else {
useQueryCopilot.getState().setIsAllocatingContainer(true);
provisionData = {
poolId: poolId,
databaseId: useTabs.getState().activeTab.collection.databaseId,
containerId: useTabs.getState().activeTab.collection.id(),
mode: mode,
};
}
connectionInfo = await this.phoenixClient.allocateContainer(provisionData); connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
if (!connectionInfo?.data?.phoenixServiceUrl) { if (!connectionInfo?.data?.phoenixServiceUrl) {
throw new Error(`PhoenixServiceUrl is invalid!`); throw new Error(`PhoenixServiceUrl is invalid!`);
@@ -442,21 +451,19 @@ export default class Explorer {
error: getErrorMessage(error), error: getErrorMessage(error),
errorStack: getErrorStack(error), errorStack: getErrorStack(error),
}); });
if (shouldUseNotebookStates) { connectionStatus.status = ConnectionStatusType.Failed;
connectionStatus.status = ConnectionStatusType.Failed; shouldUseNotebookStates
shouldUseNotebookStates ? useNotebook.getState().resetContainerConnection(connectionStatus)
? useNotebook.getState().resetContainerConnection(connectionStatus) : useQueryCopilot.getState().resetContainerConnection();
: useQueryCopilot.getState().resetContainerConnection(); if (error?.status === HttpStatusCodes.Forbidden && error.message) {
if (error?.status === HttpStatusCodes.Forbidden && error.message) { useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`);
useDialog.getState().showOkModalDialog("Connection Failed", `${error.message}`); } else {
} else { useDialog
useDialog .getState()
.getState() .showOkModalDialog(
.showOkModalDialog( "Connection Failed",
"Connection Failed", "We are unable to connect to the temporary workspace. Please try again in a few minutes. If the error persists, file a support ticket.",
"We are unable to connect to the temporary workspace. Please try again in a few minutes. If the error persists, file a support ticket.", );
);
}
} }
throw error; throw error;
} finally { } finally {
@@ -470,11 +477,11 @@ export default class Explorer {
} }
} }
public async setNotebookInfo( private async setNotebookInfo(
shouldUseNotebookStates: boolean, shouldUseNotebookStates: boolean,
connectionInfo: IResponse<IPhoenixServiceInfo>, connectionInfo: IResponse<IPhoenixServiceInfo>,
connectionStatus: DataModels.ContainerConnectionInfo, connectionStatus: DataModels.ContainerConnectionInfo,
): Promise<void> { ) {
const containerData = { const containerData = {
forwardingId: connectionInfo.data.forwardingId, forwardingId: connectionInfo.data.forwardingId,
dbAccountName: userContext.databaseAccount.name, dbAccountName: userContext.databaseAccount.name,
@@ -495,7 +502,6 @@ export default class Explorer {
shouldUseNotebookStates shouldUseNotebookStates
? useNotebook.getState().setNotebookServerInfo(noteBookServerInfo) ? useNotebook.getState().setNotebookServerInfo(noteBookServerInfo)
: useQueryCopilot.getState().setNotebookServerInfo(noteBookServerInfo); : useQueryCopilot.getState().setNotebookServerInfo(noteBookServerInfo);
shouldUseNotebookStates && shouldUseNotebookStates &&
this.notebookManager?.notebookClient this.notebookManager?.notebookClient
.getMemoryUsage() .getMemoryUsage()
@@ -1358,24 +1364,6 @@ export default class Explorer {
await this.refreshSampleData(); await this.refreshSampleData();
} }
public async configureCopilot(): Promise<void> {
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
return;
}
const copilotEnabledPromise = getCopilotEnabled();
const copilotUserDBEnabledPromise = isCopilotFeatureRegistered(userContext.subscriptionId);
const [copilotEnabled, copilotUserDBEnabled] = await Promise.all([
copilotEnabledPromise,
copilotUserDBEnabledPromise,
]);
const copilotSampleDBEnabled = LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true";
useQueryCopilot.getState().setCopilotEnabled(copilotEnabled && copilotUserDBEnabled);
useQueryCopilot.getState().setCopilotUserDBEnabled(copilotUserDBEnabled);
useQueryCopilot
.getState()
.setCopilotSampleDBEnabled(copilotEnabled && copilotUserDBEnabled && copilotSampleDBEnabled);
}
public async refreshSampleData(): Promise<void> { public async refreshSampleData(): Promise<void> {
if (!userContext.sampleDataConnectionInfo) { if (!userContext.sampleDataConnectionInfo) {
return; return;

View File

@@ -367,7 +367,7 @@ describe("GraphExplorer", () => {
}); });
it("should submit g.V() as docdb query with proper parameters", () => { it("should submit g.V() as docdb query with proper parameters", () => {
expect(queryDocuments).toHaveBeenCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, { expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE, maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: true, enableCrossPartitionQuery: true,
}); });
@@ -404,7 +404,7 @@ describe("GraphExplorer", () => {
}); });
it("should submit g.V() as docdb query with proper parameters", () => { it("should submit g.V() as docdb query with proper parameters", () => {
expect(queryDocuments).toHaveBeenCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, { expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE, maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: true, enableCrossPartitionQuery: true,
}); });

View File

@@ -1163,12 +1163,15 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
)}"`, )}"`,
).then( ).then(
(documents: DataModels.DocumentId[]) => { (documents: DataModels.DocumentId[]) => {
$.each(documents, (index: number, doc: any) => { $.each(
newIconsMap[doc["_graph_icon_property_value"]] = { documents,
data: doc["icon"], (index: number, doc: { _graph_icon_property_value: string; icon: string; format: string }) => {
format: doc["format"], newIconsMap[doc["_graph_icon_property_value"]] = {
}; data: doc["icon"],
}); format: doc["format"],
};
},
);
// Update graph configuration // Update graph configuration
this.setState({ this.setState({

View File

@@ -43,7 +43,7 @@ describe("Graph Style Component", () => {
expect(asFragment).toMatchSnapshot(); expect(asFragment).toMatchSnapshot();
}); });
it("should render node properties dropdown list", () => { it("should render node properties dropdown list ", () => {
const dropDownList = screen.getByText("Show vertex (node) as"); const dropDownList = screen.getByText("Show vertex (node) as");
expect(dropDownList).toBeDefined(); expect(dropDownList).toBeDefined();
}); });

View File

@@ -24,21 +24,16 @@ interface Props {
export interface CommandBarStore { export interface CommandBarStore {
contextButtons: CommandButtonComponentProps[]; contextButtons: CommandButtonComponentProps[];
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => void; setContextButtons: (contextButtons: CommandButtonComponentProps[]) => void;
isHidden: boolean;
setIsHidden: (isHidden: boolean) => void;
} }
export const useCommandBar: UseStore<CommandBarStore> = create((set) => ({ export const useCommandBar: UseStore<CommandBarStore> = create((set) => ({
contextButtons: [], contextButtons: [],
setContextButtons: (contextButtons: CommandButtonComponentProps[]) => set((state) => ({ ...state, contextButtons })), setContextButtons: (contextButtons: CommandButtonComponentProps[]) => set((state) => ({ ...state, contextButtons })),
isHidden: false,
setIsHidden: (isHidden: boolean) => set((state) => ({ ...state, isHidden })),
})); }));
export const CommandBar: React.FC<Props> = ({ container }: Props) => { export const CommandBar: React.FC<Props> = ({ container }: Props) => {
const selectedNodeState = useSelectedNode(); const selectedNodeState = useSelectedNode();
const buttons = useCommandBar((state) => state.contextButtons); const buttons = useCommandBar((state) => state.contextButtons);
const isHidden = useCommandBar((state) => state.isHidden);
const backgroundColor = StyleConstants.BaseLight; const backgroundColor = StyleConstants.BaseLight;
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") { if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
@@ -47,7 +42,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
? CommandBarComponentButtonFactory.createPostgreButtons(container) ? CommandBarComponentButtonFactory.createPostgreButtons(container)
: CommandBarComponentButtonFactory.createVCoreMongoButtons(container); : CommandBarComponentButtonFactory.createVCoreMongoButtons(container);
return ( return (
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}> <div className="commandBarContainer">
<FluentCommandBar <FluentCommandBar
ariaLabel="Use left and right arrow keys to navigate between commands" ariaLabel="Use left and right arrow keys to navigate between commands"
items={CommandBarUtil.convertButton(buttons, backgroundColor)} items={CommandBarUtil.convertButton(buttons, backgroundColor)}
@@ -96,7 +91,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
? { ? {
root: { root: {
backgroundColor: "transparent", backgroundColor: "transparent",
padding: "2px 8px 0px 8px", padding: "0px 14px 0px 14px",
}, },
} }
: { : {
@@ -106,7 +101,7 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
}; };
return ( return (
<div className="commandBarContainer" style={{ display: isHidden ? "none" : "initial" }}> <div className="commandBarContainer">
<FluentCommandBar <FluentCommandBar
ariaLabel="Use left and right arrow keys to navigate between commands" ariaLabel="Use left and right arrow keys to navigate between commands"
items={uiFabricStaticButtons.concat(uiFabricTabsButtons)} items={uiFabricStaticButtons.concat(uiFabricTabsButtons)}

View File

@@ -345,7 +345,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
describe("Open Postgres and vCore Mongo buttons", () => { describe("Open Postgres and vCore Mongo buttons", () => {
const openPostgresShellButtonLabel = "Open PSQL shell"; const openPostgresShellButtonLabel = "Open PSQL shell";
const openVCoreMongoShellButtonLabel = "Open MongoDB (vCore) shell"; const openVCoreMongoShellButtonLabel = "Open MongoDB (vcore) shell";
beforeAll(() => { beforeAll(() => {
mockExplorer = {} as Explorer; mockExplorer = {} as Explorer;

View File

@@ -1,3 +1,6 @@
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { ReactTabKind, useTabs } from "hooks/useTabs";
import * as React from "react"; import * as React from "react";
import AddCollectionIcon from "../../../../images/AddCollection.svg"; import AddCollectionIcon from "../../../../images/AddCollection.svg";
import AddDatabaseIcon from "../../../../images/AddDatabase.svg"; import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
@@ -47,36 +50,31 @@ export function createStaticCommandBarButtons(
return createStaticCommandBarButtonsForResourceToken(container, selectedNodeState); return createStaticCommandBarButtonsForResourceToken(container, selectedNodeState);
} }
const newCollectionBtn = createNewCollectionGroup(container);
const buttons: CommandButtonComponentProps[] = []; const buttons: CommandButtonComponentProps[] = [];
// Avoid starting with a divider buttons.push(newCollectionBtn);
const addDivider = () => { if (
if (buttons.length > 0) { configContext.platform !== Platform.Fabric &&
userContext.apiType !== "Tables" &&
userContext.apiType !== "Cassandra"
) {
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
if (addSynapseLink) {
buttons.push(createDivider()); buttons.push(createDivider());
} buttons.push(addSynapseLink);
};
if (configContext.platform !== Platform.Fabric) {
const newCollectionBtn = createNewCollectionGroup(container);
buttons.push(newCollectionBtn);
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
if (addSynapseLink) {
addDivider();
buttons.push(addSynapseLink);
}
}
if (userContext.apiType !== "Tables") {
newCollectionBtn.children = [createNewCollectionGroup(container)];
const newDatabaseBtn = createNewDatabase(container);
newCollectionBtn.children.push(newDatabaseBtn);
} }
} }
if (userContext.apiType !== "Tables") {
newCollectionBtn.children = [createNewCollectionGroup(container)];
const newDatabaseBtn = createNewDatabase(container);
newCollectionBtn.children.push(newDatabaseBtn);
}
if (useNotebook.getState().isNotebookEnabled) { if (useNotebook.getState().isNotebookEnabled) {
addDivider(); buttons.push(createDivider());
const notebookButtons: CommandButtonComponentProps[] = []; const notebookButtons: CommandButtonComponentProps[] = [];
const newNotebookButton = createNewNotebookButton(container); const newNotebookButton = createNewNotebookButton(container);
@@ -130,12 +128,12 @@ export function createStaticCommandBarButtons(
const isQuerySupported = userContext.apiType === "SQL" || userContext.apiType === "Gremlin"; const isQuerySupported = userContext.apiType === "SQL" || userContext.apiType === "Gremlin";
if (isQuerySupported) { if (isQuerySupported) {
addDivider(); buttons.push(createDivider());
const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState); const newSqlQueryBtn = createNewSQLQueryButton(selectedNodeState);
buttons.push(newSqlQueryBtn); buttons.push(newSqlQueryBtn);
} }
if (isQuerySupported && selectedNodeState.findSelectedCollection() && configContext.platform !== Platform.Fabric) { if (isQuerySupported && selectedNodeState.findSelectedCollection()) {
const openQueryBtn = createOpenQueryButton(container); const openQueryBtn = createOpenQueryButton(container);
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()]; openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
buttons.push(openQueryBtn); buttons.push(openQueryBtn);
@@ -196,22 +194,18 @@ export function createContextCommandBarButtons(
} }
export function createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] { export function createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = const buttons: CommandButtonComponentProps[] = [
configContext.platform === Platform.Fabric && userContext.fabricContext?.isReadOnly {
? [] iconSrc: SettingsIcon,
: [ iconAlt: "Settings",
{ onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane />),
iconSrc: SettingsIcon, commandButtonLabel: undefined,
iconAlt: "Settings", ariaLabel: "Settings",
onCommandClick: () => tooltipText: "Settings",
useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={container} />), hasPopup: true,
commandButtonLabel: undefined, disabled: false,
ariaLabel: "Settings", },
tooltipText: "Settings", ];
hasPopup: true,
disabled: false,
},
];
const showOpenFullScreen = const showOpenFullScreen =
configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin"; configContext.platform === Platform.Portal && !isRunningOnNationalCloud() && userContext.apiType !== "Gremlin";
@@ -338,8 +332,13 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
iconSrc: AddSqlQueryIcon, iconSrc: AddSqlQueryIcon,
iconAlt: label, iconAlt: label,
onCommandClick: () => { onCommandClick: () => {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection(); if (useSelectedNode.getState().isQueryCopilotCollectionSelected()) {
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection); useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
} else {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
}
}, },
commandButtonLabel: label, commandButtonLabel: label,
ariaLabel: label, ariaLabel: label,
@@ -519,7 +518,7 @@ function createOpenTerminalButtonByKind(
case ViewModels.TerminalKind.Postgres: case ViewModels.TerminalKind.Postgres:
return "PSQL"; return "PSQL";
case ViewModels.TerminalKind.VCoreMongo: case ViewModels.TerminalKind.VCoreMongo:
return "MongoDB (vCore)"; return "MongoDB (vcore)";
default: default:
return ""; return "";
} }

View File

@@ -35,7 +35,7 @@ describe("CommandBarUtil tests", () => {
// Click gets called // Click gets called
converted.onClick(); converted.onClick();
expect(btn.onCommandClick).toHaveBeenCalled(); expect(btn.onCommandClick).toBeCalled();
}); });
it("should convert NavbarButtonConfig to split button", () => { it("should convert NavbarButtonConfig to split button", () => {

View File

@@ -6,7 +6,6 @@ import {
IDropdownOption, IDropdownOption,
IDropdownStyles, IDropdownStyles,
} from "@fluentui/react"; } from "@fluentui/react";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as React from "react"; import * as React from "react";
import _ from "underscore"; import _ from "underscore";
import ChevronDownIcon from "../../../../images/Chevron_down.svg"; import ChevronDownIcon from "../../../../images/Chevron_down.svg";
@@ -25,10 +24,7 @@ import { MemoryTracker } from "./MemoryTrackerComponent";
* @param btns * @param btns
*/ */
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => { export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
const buttonHeightPx = const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
configContext.platform == Platform.Fabric
? StyleConstants.FabricCommandBarButtonHeight
: StyleConstants.CommandBarButtonHeight;
const hoverColor = const hoverColor =
configContext.platform == Platform.Fabric ? StyleConstants.FabricAccentLight : StyleConstants.AccentLight; configContext.platform == Platform.Fabric ? StyleConstants.FabricAccentLight : StyleConstants.AccentLight;
@@ -61,11 +57,7 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
}, },
onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => { onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
btn.onCommandClick(ev); btn.onCommandClick(ev);
let copilotEnabled = false; TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label });
if (useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotUserDBEnabled) {
copilotEnabled = useQueryCopilot.getState().copilotEnabledforExecution;
}
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label, copilotEnabled });
}, },
key: `${btn.commandButtonLabel}${index}`, key: `${btn.commandButtonLabel}${index}`,
text: label, text: label,
@@ -115,7 +107,6 @@ export const convertButton = (btns: CommandButtonComponentProps[], backgroundCol
splitButtonContainer: { splitButtonContainer: {
marginLeft: 5, marginLeft: 5,
marginRight: 5, marginRight: 5,
height: buttonHeightPx,
}, },
}, },
className: btn.className, className: btn.className,

View File

@@ -1,14 +1,14 @@
import { makeNotebookRecord } from "@nteract/commutable";
import { actions, state } from "@nteract/core";
import * as Immutable from "immutable"; import * as Immutable from "immutable";
import { StateObservable } from "redux-observable"; import { StateObservable } from "redux-observable";
import { Subject, of } from "rxjs"; import { Subject, of } from "rxjs";
import { toArray } from "rxjs/operators"; import { toArray } from "rxjs/operators";
import { makeNotebookRecord } from "@nteract/commutable";
import { actions, state, epics } from "@nteract/core";
import * as sinon from "sinon"; import * as sinon from "sinon";
import { NotebookUtil } from "../NotebookUtil";
import { launchWebSocketKernelEpic } from "./epics";
import { CdbAppState, makeCdbRecord } from "./types"; import { CdbAppState, makeCdbRecord } from "./types";
import { launchWebSocketKernelEpic } from "./epics";
import { NotebookUtil } from "../NotebookUtil";
import { sessions } from "rx-jupyter"; import { sessions } from "rx-jupyter";
@@ -117,7 +117,7 @@ describe("launchWebSocketKernelEpic", () => {
const kernelRef = "fake"; const kernelRef = "fake";
it("launches remote kernels", async () => { it("launches remote kernels", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const cwd = "/"; const cwd = "/";
const kernelId = "123"; const kernelId = "123";
@@ -183,7 +183,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("launches any kernel with no kernelspecs in the state", async () => { it("launches any kernel with no kernelspecs in the state", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const cwd = "/"; const cwd = "/";
const kernelId = "123"; const kernelId = "123";
@@ -236,7 +236,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("launches no kernel if no kernel is specified and state has no kernelspecs", async () => { it("launches no kernel if no kernel is specified and state has no kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const cwd = "/"; const cwd = "/";
const kernelId = "123"; const kernelId = "123";
@@ -289,7 +289,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("emits an error if backend returns an error", async () => { it("emits an error if backend returns an error", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const cwd = "/"; const cwd = "/";
const action$ = of( const action$ = of(
@@ -388,7 +388,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("launches supported kernel in kernelspecs", async () => { it("launches supported kernel in kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
@@ -409,7 +409,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("launches undefined kernel uses default kernel from kernelspecs", async () => { it("launches undefined kernel uses default kernel from kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
@@ -431,7 +431,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("launches unsupported kernel uses default kernel from kernelspecs", async () => { it("launches unsupported kernel uses default kernel from kernelspecs", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({
@@ -453,7 +453,7 @@ describe("launchWebSocketKernelEpic", () => {
}); });
it("launches unsupported kernel uses kernelspecs with similar name", async () => { it("launches unsupported kernel uses kernelspecs with similar name", async () => {
const state$ = new StateObservable(new Subject<CdbAppState>() as any, initialState); const state$ = new StateObservable(new Subject<CdbAppState>(), initialState);
const action$ = of( const action$ = of(
actions.launchKernelByName({ actions.launchKernelByName({

View File

@@ -69,8 +69,8 @@ const addInitialCodeCellEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<{} | actions.CreateCellBelow> => { ): Observable<{} | actions.CreateCellBelow> => {
return action$.pipe( return action$.pipe(
ofType(actions.FETCH_CONTENT_FULFILLED) as any, ofType(actions.FETCH_CONTENT_FULFILLED),
mergeMap((action: any) => { mergeMap((action) => {
const state = state$.value; const state = state$.value;
const contentRef = action.payload.contentRef; const contentRef = action.payload.contentRef;
const model = selectors.model(state, { contentRef }); const model = selectors.model(state, { contentRef });
@@ -116,7 +116,7 @@ const formWebSocketURL = (serverConfig: NotebookServiceConfig, kernelId: string,
*/ */
export const acquireKernelInfoEpic = (action$: Observable<actions.NewKernelAction>) => { export const acquireKernelInfoEpic = (action$: Observable<actions.NewKernelAction>) => {
return action$.pipe( return action$.pipe(
ofType(actions.LAUNCH_KERNEL_SUCCESSFUL) as any, ofType(actions.LAUNCH_KERNEL_SUCCESSFUL),
switchMap((action: actions.NewKernelAction) => { switchMap((action: actions.NewKernelAction) => {
const { const {
payload: { payload: {
@@ -271,9 +271,9 @@ export const launchWebSocketKernelEpic = (
state$: StateObservable<CdbAppState>, state$: StateObservable<CdbAppState>,
) => { ) => {
return action$.pipe( return action$.pipe(
ofType(actions.LAUNCH_KERNEL_BY_NAME) as any, ofType(actions.LAUNCH_KERNEL_BY_NAME),
// Only accept jupyter servers for the host with this epic // Only accept jupyter servers for the host with this epic
filter(() => selectors.isCurrentHostJupyter(state$.value as never)), filter(() => selectors.isCurrentHostJupyter(state$.value)),
switchMap((action: actions.LaunchKernelByNameAction) => { switchMap((action: actions.LaunchKernelByNameAction) => {
const state = state$.value; const state = state$.value;
const host = selectors.currentHost(state); const host = selectors.currentHost(state);
@@ -382,7 +382,7 @@ export const restartWebSocketKernelEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
) => ) =>
action$.pipe( action$.pipe(
ofType(actions.RESTART_KERNEL) as any, ofType(actions.RESTART_KERNEL),
concatMap((action: actions.RestartKernel) => { concatMap((action: actions.RestartKernel) => {
const state = state$.value; const state = state$.value;
@@ -449,7 +449,7 @@ export const restartWebSocketKernelEpic = (
}); });
const awaitKernelReady = action$.pipe( const awaitKernelReady = action$.pipe(
ofType(actions.LAUNCH_KERNEL_SUCCESSFUL) as any, ofType(actions.LAUNCH_KERNEL_SUCCESSFUL),
filter((action: actions.NewKernelAction | actions.RestartKernel) => action.payload.kernelRef === newKernelRef), filter((action: actions.NewKernelAction | actions.RestartKernel) => action.payload.kernelRef === newKernelRef),
take(1), take(1),
timeout(60000), // If kernel doesn't come up within this interval we will abort follow-on actions. timeout(60000), // If kernel doesn't come up within this interval we will abort follow-on actions.
@@ -492,9 +492,9 @@ const changeWebSocketKernelEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
) => { ) => {
return action$.pipe( return action$.pipe(
ofType(actions.CHANGE_KERNEL_BY_NAME) as any, ofType(actions.CHANGE_KERNEL_BY_NAME),
// Only accept jupyter servers for the host with this epic // Only accept jupyter servers for the host with this epic
filter(() => selectors.isCurrentHostJupyter(state$.value as never)), filter(() => selectors.isCurrentHostJupyter(state$.value)),
switchMap((action: actions.ChangeKernelByName) => { switchMap((action: actions.ChangeKernelByName) => {
const { const {
payload: { contentRef, oldKernelRef, kernelSpecName }, payload: { contentRef, oldKernelRef, kernelSpecName },
@@ -574,8 +574,8 @@ const focusInitialCodeCellEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<{} | actions.FocusCell> => { ): Observable<{} | actions.FocusCell> => {
return action$.pipe( return action$.pipe(
ofType(actions.CREATE_CELL_APPEND) as any, ofType(actions.CREATE_CELL_APPEND),
mergeMap((action: any) => { mergeMap((action) => {
const state = state$.value; const state = state$.value;
const contentRef = action.payload.contentRef; const contentRef = action.payload.contentRef;
const model = selectors.model(state, { contentRef }); const model = selectors.model(state, { contentRef });
@@ -616,8 +616,8 @@ const notificationsToUserEpic = (action$: Observable<any>, state$: StateObservab
actions.SAVE_FULFILLED, actions.SAVE_FULFILLED,
actions.SAVE_FAILED, actions.SAVE_FAILED,
actions.FETCH_CONTENT_FAILED, actions.FETCH_CONTENT_FAILED,
) as any, ),
mergeMap((action: any) => { mergeMap((action) => {
switch (action.type) { switch (action.type) {
case actions.RESTART_KERNEL_SUCCESSFUL: { case actions.RESTART_KERNEL_SUCCESSFUL: {
const title = "Kernel restart"; const title = "Kernel restart";
@@ -662,8 +662,8 @@ const handleKernelConnectionLostEpic = (
state$: StateObservable<CdbAppState>, state$: StateObservable<CdbAppState>,
): Observable<CdbActions.UpdateKernelRestartDelayAction | actions.RestartKernel | {}> => { ): Observable<CdbActions.UpdateKernelRestartDelayAction | actions.RestartKernel | {}> => {
return action$.pipe( return action$.pipe(
ofType(actions.UPDATE_DISPLAY_FAILED) as any, ofType(actions.UPDATE_DISPLAY_FAILED),
mergeMap((action: any) => { mergeMap((action) => {
const state = state$.value; const state = state$.value;
const msg = "Notebook was disconnected from kernel"; const msg = "Notebook was disconnected from kernel";
@@ -721,8 +721,8 @@ export const cleanKernelOnConnectionLostEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<actions.KillKernelSuccessful> => { ): Observable<actions.KillKernelSuccessful> => {
return action$.pipe( return action$.pipe(
ofType(actions.UPDATE_DISPLAY_FAILED) as any, ofType(actions.UPDATE_DISPLAY_FAILED),
switchMap((action: any) => { switchMap((action) => {
const contentRef = action.payload.contentRef; const contentRef = action.payload.contentRef;
const kernelRef = selectors.kernelRefByContentRef(state$.value, { contentRef }); const kernelRef = selectors.kernelRefByContentRef(state$.value, { contentRef });
return of( return of(
@@ -744,8 +744,8 @@ const executeFocusedCellAndFocusNextEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<{} | actions.FocusNextCellEditor> => { ): Observable<{} | actions.FocusNextCellEditor> => {
return action$.pipe( return action$.pipe(
ofType(CdbActions.EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT) as any, ofType(CdbActions.EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT),
mergeMap((action: any) => { mergeMap((action) => {
const contentRef = action.payload.contentRef; const contentRef = action.payload.contentRef;
return concat( return concat(
of(actions.executeFocusedCell({ contentRef })), of(actions.executeFocusedCell({ contentRef })),
@@ -765,8 +765,8 @@ const closeUnsupportedMimetypesEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<{}> => { ): Observable<{}> => {
return action$.pipe( return action$.pipe(
ofType(actions.FETCH_CONTENT_FULFILLED) as any, ofType(actions.FETCH_CONTENT_FULFILLED),
mergeMap((action: any) => { mergeMap((action) => {
const mimetype = action.payload.model.mimetype; const mimetype = action.payload.model.mimetype;
if (!TextFile.handles(mimetype)) { if (!TextFile.handles(mimetype)) {
const filepath = action.payload.filepath; const filepath = action.payload.filepath;
@@ -796,8 +796,8 @@ const closeContentFailedToFetchEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<{}> => { ): Observable<{}> => {
return action$.pipe( return action$.pipe(
ofType(actions.FETCH_CONTENT_FAILED) as any, ofType(actions.FETCH_CONTENT_FAILED),
mergeMap((action: any) => { mergeMap((action) => {
const filepath = action.payload.filepath; const filepath = action.payload.filepath;
// Close tab and show error message // Close tab and show error message
useTabs useTabs
@@ -818,7 +818,7 @@ const traceNotebookTelemetryEpic = (
state$: StateObservable<CdbAppState>, state$: StateObservable<CdbAppState>,
): Observable<{}> => { ): Observable<{}> => {
return action$.pipe( return action$.pipe(
ofType(cdbActions.TRACE_NOTEBOOK_TELEMETRY) as any, ofType(cdbActions.TRACE_NOTEBOOK_TELEMETRY),
mergeMap((action: cdbActions.TraceNotebookTelemetryAction) => { mergeMap((action: cdbActions.TraceNotebookTelemetryAction) => {
const state = state$.value; const state = state$.value;
@@ -844,7 +844,7 @@ const traceNotebookInfoEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<{} | cdbActions.TraceNotebookTelemetryAction> => { ): Observable<{} | cdbActions.TraceNotebookTelemetryAction> => {
return action$.pipe( return action$.pipe(
ofType(actions.FETCH_CONTENT_FULFILLED) as any, ofType(actions.FETCH_CONTENT_FULFILLED),
mergeMap((action: { payload: any }) => { mergeMap((action: { payload: any }) => {
const state = state$.value; const state = state$.value;
const contentRef = action.payload.contentRef; const contentRef = action.payload.contentRef;
@@ -897,7 +897,7 @@ const traceNotebookKernelEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<cdbActions.TraceNotebookTelemetryAction> => { ): Observable<cdbActions.TraceNotebookTelemetryAction> => {
return action$.pipe( return action$.pipe(
ofType(actions.LAUNCH_KERNEL_SUCCESSFUL) as any, ofType(actions.LAUNCH_KERNEL_SUCCESSFUL),
mergeMap((action: { payload: any; type: string }) => { mergeMap((action: { payload: any; type: string }) => {
return of( return of(
cdbActions.traceNotebookTelemetry({ cdbActions.traceNotebookTelemetry({
@@ -917,8 +917,8 @@ const resetCellStatusOnExecuteCanceledEpic = (
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<actions.UpdateCellStatus> => { ): Observable<actions.UpdateCellStatus> => {
return action$.pipe( return action$.pipe(
ofType(actions.EXECUTE_CANCELED) as any, ofType(actions.EXECUTE_CANCELED),
mergeMap((action: any) => { mergeMap((action) => {
const contentRef = action.payload.contentRef; const contentRef = action.payload.contentRef;
const model = state$.value.core.entities.contents.byRef.get(contentRef).model; const model = state$.value.core.entities.contents.byRef.get(contentRef).model;
let busyCellIds: string[] = []; let busyCellIds: string[] = [];
@@ -960,8 +960,8 @@ export function autoSaveCurrentContentEpic(
state$: StateObservable<AppState>, state$: StateObservable<AppState>,
): Observable<actions.Save> { ): Observable<actions.Save> {
return state$.pipe( return state$.pipe(
map((state) => autoSaveInterval(state)) as any, map((state) => autoSaveInterval(state)),
switchMap((time) => interval(time as number)) as any, switchMap((time) => interval(time)),
mergeMap(() => { mergeMap(() => {
const state = state$.value; const state = state$.value;
return from( return from(
@@ -976,7 +976,7 @@ export function autoSaveCurrentContentEpic(
) )
.keys(), .keys(),
); );
}) as any, }),
filter((contentRef: ContentRef) => { filter((contentRef: ContentRef) => {
const model = selectors.model(state$.value, { contentRef }); const model = selectors.model(state$.value, { contentRef });
const content = selectors.content(state$.value, { contentRef }); const content = selectors.content(state$.value, { contentRef });
@@ -985,12 +985,12 @@ export function autoSaveCurrentContentEpic(
model.type === "notebook" && model.type === "notebook" &&
NotebookUtil.getContentProviderType(content.filepath) !== NotebookContentProviderType.JupyterContentProviderType NotebookUtil.getContentProviderType(content.filepath) !== NotebookContentProviderType.JupyterContentProviderType
) { ) {
return selectors.notebook.isDirty(model as never); return selectors.notebook.isDirty(model);
} }
return false; return false;
}) as any, }),
map((contentRef: ContentRef) => actions.save({ contentRef })) as any, map((contentRef: ContentRef) => actions.save({ contentRef })),
) as any; );
} }
export const allEpics = [ export const allEpics = [

View File

@@ -34,11 +34,11 @@ export default function configureStore(
const protect = (epic: Epic) => { const protect = (epic: Epic) => {
return (action$: Observable<any>, state$: any, dependencies: any) => return (action$: Observable<any>, state$: any, dependencies: any) =>
epic(action$ as any, state$, dependencies).pipe( epic(action$, state$, dependencies).pipe(
catchError((error, caught) => { catchError((error, caught) => {
traceFailure("Epic failure", error); traceFailure("Epic failure", error);
return caught; return caught;
}) as any, }),
); );
}; };
@@ -52,7 +52,7 @@ export default function configureStore(
}; };
const protectEpics = (epics: Epic[]): Epic[] => { const protectEpics = (epics: Epic[]): Epic[] => {
return epics.map((epic) => protect(epic)) as any; return epics.map((epic) => protect(epic));
}; };
const filteredCoreEpics = getCoreEpics(autoStartKernelOnNotebookOpen); const filteredCoreEpics = getCoreEpics(autoStartKernelOnNotebookOpen);
@@ -64,7 +64,7 @@ export default function configureStore(
core: coreReducer as any, core: coreReducer as any,
cdb: cdbReducer, cdb: cdbReducer,
}, },
epics: protectEpics([...filteredCoreEpics, ...allEpics] as any), epics: protectEpics([...filteredCoreEpics, ...allEpics]),
epicDependencies: { contentProvider }, epicDependencies: { contentProvider },
epicMiddleware: customMiddlewares.concat(catchErrorMiddleware), epicMiddleware: customMiddlewares.concat(catchErrorMiddleware),
enhancer: composeEnhancers, enhancer: composeEnhancers,
@@ -106,5 +106,5 @@ export const getCoreEpics = (autoStartKernelOnNotebookOpen: boolean): Epic[] =>
filteredCoreEpics.push(coreEpics.launchKernelWhenNotebookSetEpic); filteredCoreEpics.push(coreEpics.launchKernelWhenNotebookSetEpic);
} }
return filteredCoreEpics as any; return filteredCoreEpics;
}; };

View File

@@ -154,7 +154,7 @@ function openPane(action: ActionContracts.OpenPane, explorer: Explorer) {
action.paneKind === ActionContracts.PaneKind.GlobalSettings || action.paneKind === ActionContracts.PaneKind.GlobalSettings ||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.GlobalSettings] action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.GlobalSettings]
) { ) {
useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={explorer} />); useSidePanel.getState().openSidePanel("Settings", <SettingsPane />);
} }
} }

View File

@@ -114,8 +114,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
super(props); super(props);
this.state = { this.state = {
createNewDatabase: createNewDatabase: userContext.apiType !== "Tables" && !this.props.databaseId,
userContext.apiType !== "Tables" && configContext.platform !== Platform.Fabric && !this.props.databaseId,
newDatabaseId: props.isQuickstart ? this.getSampleDBName() : "", newDatabaseId: props.isQuickstart ? this.getSampleDBName() : "",
isSharedThroughputChecked: this.getSharedThroughputDefault(), isSharedThroughputChecked: this.getSharedThroughputDefault(),
selectedDatabaseId: selectedDatabaseId:
@@ -275,38 +274,36 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</TooltipHost> </TooltipHost>
</Stack> </Stack>
{configContext.platform !== Platform.Fabric && ( <Stack horizontal verticalAlign="center">
<Stack horizontal verticalAlign="center"> <div role="radiogroup">
<div role="radiogroup"> <input
<input className="panelRadioBtn"
className="panelRadioBtn" checked={this.state.createNewDatabase}
checked={this.state.createNewDatabase} aria-label="Create new database"
aria-label="Create new database" aria-checked={this.state.createNewDatabase}
aria-checked={this.state.createNewDatabase} name="databaseType"
name="databaseType" type="radio"
type="radio" role="radio"
role="radio" id="databaseCreateNew"
id="databaseCreateNew" tabIndex={0}
tabIndex={0} onChange={this.onCreateNewDatabaseRadioBtnChange.bind(this)}
onChange={this.onCreateNewDatabaseRadioBtnChange.bind(this)} />
/> <span className="panelRadioBtnLabel">Create new</span>
<span className="panelRadioBtnLabel">Create new</span>
<input <input
className="panelRadioBtn" className="panelRadioBtn"
checked={!this.state.createNewDatabase} checked={!this.state.createNewDatabase}
aria-label="Use existing database" aria-label="Use existing database"
aria-checked={!this.state.createNewDatabase} aria-checked={!this.state.createNewDatabase}
name="databaseType" name="databaseType"
type="radio" type="radio"
role="radio" role="radio"
tabIndex={0} tabIndex={0}
onChange={this.onUseExistingDatabaseRadioBtnChange.bind(this)} onChange={this.onUseExistingDatabaseRadioBtnChange.bind(this)}
/> />
<span className="panelRadioBtnLabel">Use existing</span> <span className="panelRadioBtnLabel">Use existing</span>
</div> </div>
</Stack> </Stack>
)}
{this.state.createNewDatabase && ( {this.state.createNewDatabase && (
<Stack className="panelGroupSpacing"> <Stack className="panelGroupSpacing">
@@ -1431,6 +1428,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
this.setState({ isExecuting: false }); this.setState({ isExecuting: false });
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey); TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
useSidePanel.getState().closeSidePanel(); useSidePanel.getState().closeSidePanel();
// open NPS Survey Dialog once the collection is created
this.props.explorer.openNPSSurveyDialog();
} catch (error) { } catch (error) {
const errorMessage: string = getErrorMessage(error); const errorMessage: string = getErrorMessage(error);
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true }); this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });

View File

@@ -1,11 +1,12 @@
import { Checkbox, Stack, Text, TextField } from "@fluentui/react"; import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
import React, { FunctionComponent, useEffect, useState } from "react"; import React, { FunctionComponent, useEffect, useState } from "react";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { createDatabase } from "../../../Common/dataAccess/createDatabase";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip"; import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
import { createDatabase } from "../../../Common/dataAccess/createDatabase";
import * as DataModels from "../../../Contracts/DataModels"; import * as DataModels from "../../../Contracts/DataModels";
import { SubscriptionType } from "../../../Contracts/SubscriptionType"; import { SubscriptionType } from "../../../Contracts/SubscriptionType";
import { useSidePanel } from "../../../hooks/useSidePanel";
import * as SharedConstants from "../../../Shared/Constants"; import * as SharedConstants from "../../../Shared/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -13,7 +14,6 @@ import { userContext } from "../../../UserContext";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { isServerlessAccount } from "../../../Utils/CapabilityUtils"; import { isServerlessAccount } from "../../../Utils/CapabilityUtils";
import { getUpsellMessage } from "../../../Utils/PricingUtils"; import { getUpsellMessage } from "../../../Utils/PricingUtils";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput"; import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";
import { useDatabases } from "../../useDatabases"; import { useDatabases } from "../../useDatabases";
@@ -63,6 +63,9 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
}, },
subscriptionType: SubscriptionType[subscriptionType], subscriptionType: SubscriptionType[subscriptionType],
subscriptionQuotaId: userContext.quotaId, subscriptionQuotaId: userContext.quotaId,
defaultsCheck: {
flight: userContext.addCollectionFlight,
},
dataExplorerArea: Constants.Areas.ContextualPane, dataExplorerArea: Constants.Areas.ContextualPane,
}; };
@@ -72,6 +75,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
subscriptionQuotaId: userContext.quotaId, subscriptionQuotaId: userContext.quotaId,
defaultsCheck: { defaultsCheck: {
throughput, throughput,
flight: userContext.addCollectionFlight,
}, },
dataExplorerArea: Constants.Areas.ContextualPane, dataExplorerArea: Constants.Areas.ContextualPane,
}; };

View File

@@ -23,7 +23,7 @@ describe("Cassandra add collection pane test", () => {
expect(screen.getByRole("combobox", { name: "Choose existing keyspace id" })).toBeDefined(); expect(screen.getByRole("combobox", { name: "Choose existing keyspace id" })).toBeDefined();
}); });
it("enter Keyspace name", () => { it("enter Keyspace name ", () => {
fireEvent.change(screen.getByRole("textbox", { name: "Keyspace id" }), { target: { value: "table1" } }); fireEvent.change(screen.getByRole("textbox", { name: "Keyspace id" }), { target: { value: "table1" } });
expect(screen.getByText("CREATE TABLE table1.")).toBeDefined(); expect(screen.getByText("CREATE TABLE table1.")).toBeDefined();
}); });

View File

@@ -59,6 +59,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
defaultsCheck: { defaultsCheck: {
storage: "u", storage: "u",
throughput: newKeySpaceThroughput || tableThroughput, throughput: newKeySpaceThroughput || tableThroughput,
flight: userContext.addCollectionFlight,
}, },
dataExplorerArea: Constants.Areas.ContextualPane, dataExplorerArea: Constants.Areas.ContextualPane,
}; };
@@ -313,7 +314,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
</Stack> </Stack>
{!isServerlessAccount() && isKeyspaceShared && !keyspaceCreateNew && ( {!isServerlessAccount() && isKeyspaceShared && !keyspaceCreateNew && (
<Stack horizontal verticalAlign="center"> <Stack>
<input <input
type="checkbox" type="checkbox"
id="tableSharedThroughput" id="tableSharedThroughput"

View File

@@ -1,396 +0,0 @@
import {
DefaultButton,
DirectionalHint,
Dropdown,
IDropdownOption,
Icon,
IconButton,
Link,
Stack,
Text,
TooltipHost,
} from "@fluentui/react";
import * as Constants from "Common/Constants";
import { handleError } from "Common/ErrorHandlingUtils";
import { createCollection } from "Common/dataAccess/createCollection";
import { DataTransferParams, initiateDataTransfer } from "Common/dataAccess/dataTransfers";
import * as DataModels from "Contracts/DataModels";
import * as ViewModels from "Contracts/ViewModels";
import {
getPartitionKeyName,
getPartitionKeyPlaceHolder,
getPartitionKeySubtext,
getPartitionKeyTooltipText,
} from "Explorer/Controls/Settings/SettingsUtils";
import Explorer from "Explorer/Explorer";
import { RightPaneForm } from "Explorer/Panes/RightPaneForm/RightPaneForm";
import { useDatabases } from "Explorer/useDatabases";
import { userContext } from "UserContext";
import { getCollectionName } from "Utils/APITypeUtils";
import { useSidePanel } from "hooks/useSidePanel";
import * as React from "react";
export interface ChangePartitionKeyPaneProps {
sourceDatabase: ViewModels.Database;
sourceCollection: ViewModels.Collection;
explorer: Explorer;
onClose: () => Promise<void>;
}
export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
sourceDatabase,
sourceCollection,
explorer,
onClose,
}) => {
const [targetCollectionId, setTargetCollectionId] = React.useState<string>();
const [createNewContainer, setCreateNewContainer] = React.useState<boolean>(true);
const [formError, setFormError] = React.useState<string>();
const [isExecuting, setIsExecuting] = React.useState<boolean>(false);
const [subPartitionKeys, setSubPartitionKeys] = React.useState<string[]>([]);
const [partitionKey, setPartitionKey] = React.useState<string>();
const getCollectionOptions = (): IDropdownOption[] => {
return sourceDatabase
.collections()
.filter((collection) => collection.id !== sourceCollection.id)
.map((collection) => ({
key: collection.id(),
text: collection.id(),
}));
};
const submit = async () => {
if (!validateInputs()) {
return;
}
setIsExecuting(true);
try {
createNewContainer && (await createContainer());
await createDataTransferJob();
await onClose();
} catch (error) {
handleError(error, "ChangePartitionKey", "Failed to start data transfer job");
}
setIsExecuting(false);
useSidePanel.getState().closeSidePanel();
};
const validateInputs = (): boolean => {
if (!createNewContainer && !targetCollectionId) {
setFormError("Choose an existing container");
return false;
}
return true;
};
const createDataTransferJob = async () => {
const jobName = `Portal_${targetCollectionId}_${Math.floor(Date.now() / 1000)}`;
const dataTransferParams: DataTransferParams = {
jobName,
apiType: userContext.apiType,
subscriptionId: userContext.subscriptionId,
resourceGroupName: userContext.resourceGroup,
accountName: userContext.databaseAccount.name,
sourceDatabaseName: sourceDatabase.id(),
sourceCollectionName: sourceCollection.id(),
targetDatabaseName: sourceDatabase.id(),
targetCollectionName: targetCollectionId,
};
await initiateDataTransfer(dataTransferParams);
};
const createContainer = async () => {
const partitionKeyString = partitionKey.trim();
const partitionKeyData: DataModels.PartitionKey = partitionKeyString
? {
paths: [partitionKeyString, ...(subPartitionKeys.length > 0 ? subPartitionKeys : [])],
kind: subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
version: 2,
}
: undefined;
const createCollectionParams: DataModels.CreateCollectionParams = {
createNewDatabase: false,
collectionId: targetCollectionId,
databaseId: sourceDatabase.id(),
databaseLevelThroughput: isSelectedDatabaseSharedThroughput(),
offerThroughput: sourceCollection.offer()?.manualThroughput,
autoPilotMaxThroughput: sourceCollection.offer()?.autoscaleMaxThroughput,
partitionKey: partitionKeyData,
};
await createCollection(createCollectionParams);
await explorer.refreshAllDatabases();
};
const isSelectedDatabaseSharedThroughput = (): boolean => {
const selectedDatabase = useDatabases
.getState()
.databases?.find((database) => database.id() === sourceDatabase.id());
return !!selectedDatabase?.offer();
};
return (
<RightPaneForm formError={formError} isExecuting={isExecuting} onSubmit={submit} submitButtonText="OK">
<Stack tokens={{ childrenGap: 10 }} className="panelMainContent">
<Text variant="small">
When changing a containers partition key, you will need to create a destination container with the correct
partition key. You may also select an existing destination container.&nbsp;
<Link
href="https://learn.microsoft.com/en-us/azure/cosmos-db/container-copy#container-copy-within-an-azure-cosmos-db-account"
target="_blank"
underline
>
Learn more
</Link>
</Text>
<Stack>
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
Database id
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`A database is analogous to a namespace. It is the unit of management for a set of ${getCollectionName(
true,
).toLocaleLowerCase()}.`}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
aria-label={`A database is analogous to a namespace. It is the unit of management for a set of ${getCollectionName(
true,
).toLocaleLowerCase()}.`}
/>
</TooltipHost>
</Stack>
<Dropdown
styles={{ title: { height: 27, lineHeight: 27 }, dropdownItem: { fontSize: 12 } }}
style={{ width: 300, fontSize: 12 }}
options={[]}
placeholder={sourceDatabase.id()}
responsiveMode={999}
disabled={true}
/>
</Stack>
<Stack className="panelGroupSpacing" horizontal verticalAlign="center">
<div role="radiogroup">
<input
className="panelRadioBtn"
checked={createNewContainer}
aria-label="Create new container"
aria-checked={createNewContainer}
name="containerType"
type="radio"
role="radio"
id="containerCreateNew"
tabIndex={0}
onChange={() => setCreateNewContainer(true)}
/>
<span className="panelRadioBtnLabel">New container</span>
<input
className="panelRadioBtn"
checked={!createNewContainer}
aria-label="Use existing container"
aria-checked={!createNewContainer}
name="containerType"
type="radio"
role="radio"
tabIndex={0}
onChange={() => setCreateNewContainer(false)}
/>
<span className="panelRadioBtnLabel">Existing container</span>
</div>
</Stack>
{createNewContainer ? (
<Stack>
<Stack className="panelGroupSpacing">
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
{`${getCollectionName()} id`}
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
>
<Icon
role="button"
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
/>
</TooltipHost>
</Stack>
<input
name="collectionId"
id="collectionId"
type="text"
aria-required
required
autoComplete="off"
pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'"
placeholder={`e.g., ${getCollectionName()}1`}
size={40}
className="panelTextField"
aria-label={`${getCollectionName()} id, Example ${getCollectionName()}1`}
value={targetCollectionId}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setTargetCollectionId(event.target.value)}
/>
</Stack>
<Stack tokens={{ childrenGap: 10 }}>
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
{getPartitionKeyName(userContext.apiType)}
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={getPartitionKeyTooltipText(userContext.apiType)}
>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
aria-label={getPartitionKeyTooltipText(userContext.apiType)}
/>
</TooltipHost>
</Stack>
<Text variant="small" aria-label="pkDescription">
{getPartitionKeySubtext(userContext.features.partitionKeyDefault, userContext.apiType)}
</Text>
<input
type="text"
id="addCollection-partitionKeyValue"
aria-required
required
size={40}
className="panelTextField"
placeholder={getPartitionKeyPlaceHolder(userContext.apiType)}
aria-label={getPartitionKeyName(userContext.apiType)}
pattern={".*"}
title={""}
value={partitionKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (!partitionKey && !event.target.value.startsWith("/")) {
setPartitionKey("/" + event.target.value);
} else {
setPartitionKey(event.target.value);
}
}}
/>
{subPartitionKeys.map((subPartitionKey: string, index: number) => {
return (
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${index}`} horizontal>
<div
style={{
width: "20px",
border: "solid",
borderWidth: "0px 0px 1px 1px",
marginRight: "5px",
}}
></div>
<input
type="text"
id="addCollection-partitionKeyValue"
key={`addCollection-partitionKeyValue_${index}`}
aria-required
required
size={40}
tabIndex={index > 0 ? 1 : 0}
className="panelTextField"
autoComplete="off"
placeholder={getPartitionKeyPlaceHolder(userContext.apiType, index)}
aria-label={getPartitionKeyName(userContext.apiType)}
pattern={".*"}
title={""}
value={subPartitionKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const keys = [...subPartitionKeys];
if (!keys[index] && !event.target.value.startsWith("/")) {
keys[index] = "/" + event.target.value.trim();
setSubPartitionKeys(keys);
} else {
keys[index] = event.target.value.trim();
setSubPartitionKeys(keys);
}
}}
/>
<IconButton
iconProps={{ iconName: "Delete" }}
style={{ height: 27 }}
onClick={() => {
const keys = subPartitionKeys.filter((uniqueKey, j) => index !== j);
setSubPartitionKeys(keys);
}}
/>
</Stack>
);
})}
<Stack className="panelGroupSpacing">
<DefaultButton
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
disabled={subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
onClick={() => setSubPartitionKeys([...subPartitionKeys, ""])}
>
Add hierarchical partition key
</DefaultButton>
{subPartitionKeys.length > 0 && (
<Text variant="small">
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> This feature allows you to
partition your data with up to three levels of keys for better data distribution. Requires .NET V3,
Java V4 SDK, or preview JavaScript V3 SDK.{" "}
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
Learn more
</Link>
</Text>
)}
</Stack>
</Stack>
</Stack>
) : (
<Stack>
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
{`${getCollectionName()}`}
</Text>
<TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge}
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
>
<Icon
role="button"
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
/>
</TooltipHost>
</Stack>
<Dropdown
styles={{ title: { height: 27, lineHeight: 27 }, dropdownItem: { fontSize: 12 } }}
style={{ width: 300, fontSize: 12 }}
placeholder="Choose an existing container"
options={getCollectionOptions()}
onChange={(event: React.FormEvent<HTMLDivElement>, collection: IDropdownOption) => {
setTargetCollectionId(collection.key as string);
setFormError("");
}}
defaultSelectedKey={targetCollectionId}
responsiveMode={999}
/>
</Stack>
)}
</Stack>
</RightPaneForm>
);
};

View File

@@ -35,11 +35,10 @@ export const DeleteCollectionConfirmationPane: FunctionComponent<DeleteCollectio
const collectionName = getCollectionName().toLocaleLowerCase(); const collectionName = getCollectionName().toLocaleLowerCase();
const paneTitle = "Delete " + collectionName; const paneTitle = "Delete " + collectionName;
const onSubmit = async (): Promise<void> => { const onSubmit = async (): Promise<void> => {
const collection = useSelectedNode.getState().findSelectedCollection(); const collection = useSelectedNode.getState().findSelectedCollection();
if (!collection || inputCollectionName !== collection.id()) { if (!collection || inputCollectionName !== collection.id()) {
const errorMessage = "Input id " + inputCollectionName + " does not match the selected " + collection.id(); const errorMessage = "Input " + collectionName + " id does not match the selected " + collectionName;
setFormError(errorMessage); setFormError(errorMessage);
NotificationConsoleUtils.logConsoleError( NotificationConsoleUtils.logConsoleError(
`Error while deleting ${collectionName} ${collection.id()}: ${errorMessage}`, `Error while deleting ${collectionName} ${collection.id()}: ${errorMessage}`,

View File

@@ -32,7 +32,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-109" className="css-53"
> >
Confirm by typing the Confirm by typing the
container container
@@ -340,19 +340,19 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
value="" value=""
> >
<div <div
className="ms-TextField is-required root-111" className="ms-TextField is-required root-55"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<div <div
className="ms-TextField-fieldGroup fieldGroup-112" className="ms-TextField-fieldGroup fieldGroup-56"
> >
<input <input
aria-invalid={false} aria-invalid={false}
aria-label="Confirm by typing the container id" aria-label="Confirm by typing the container id"
autoFocus={true} autoFocus={true}
className="ms-TextField-field field-113" className="ms-TextField-field field-57"
id="confirmCollectionId" id="confirmCollectionId"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@@ -1259,7 +1259,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1285,7 +1285,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1297,16 +1297,14 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1337,7 +1335,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"display": "inline-block", "display": "inline-block",
"padding": "0 16px", "padding": "0 16px",
"selectors": Object { "selectors": Object {
":active > span": Object { ":active > *": Object {
"left": 0, "left": 0,
"position": "relative", "position": "relative",
"top": 0, "top": 0,
@@ -1364,7 +1362,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -1389,16 +1387,14 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1432,7 +1428,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"backgroundColor": "#f3f2f1", "backgroundColor": "#f3f2f1",
"color": "#d2d0ce", "color": "#d2d0ce",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1449,7 +1445,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"border": "1px solid #106ebe", "border": "1px solid #106ebe",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"borderColor": "Highlight", "borderColor": "Highlight",
"color": "Window", "color": "Window",
@@ -1461,7 +1457,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"border": "1px solid #005a9e", "border": "1px solid #005a9e",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -1477,13 +1473,12 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"overflow": "hidden", "overflow": "hidden",
"padding": 0, "padding": 0,
"position": "absolute", "position": "absolute",
"whiteSpace": "nowrap",
"width": 1, "width": 1,
}, },
"splitButtonContainer": Array [ "splitButtonContainer": Array [
Object { Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
}, },
}, },
@@ -1494,16 +1489,14 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": "none",
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -1526,21 +1519,13 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderRight": "none", "borderRight": "none",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
}, },
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"border": "none", "border": "none",
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
"selectors": Object { "selectors": Object {
":active": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none",
},
":hover": Object {
"border": "none",
},
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid WindowText", "border": "1px solid WindowText",
@@ -1553,7 +1538,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderLeftWidth": "0", "borderLeftWidth": "0",
}, },
@@ -1566,7 +1551,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -1580,7 +1565,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -1594,7 +1579,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"border": "none", "border": "none",
"outline": "none", "outline": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
@@ -1610,7 +1595,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"color": "Window", "color": "Window",
}, },
@@ -1619,7 +1604,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
".ms-Button.is-disabled": Object { ".ms-Button.is-disabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1635,7 +1620,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
}, },
}, },
@@ -1647,7 +1632,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@@ -1660,7 +1645,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "GrayText", "backgroundColor": "GrayText",
}, },
}, },
@@ -1682,18 +1667,18 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
":hover": Object { ":hover": Object {
"backgroundColor": "#106ebe", "backgroundColor": "#106ebe",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "Highlight", "color": "Highlight",
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Canvas", "backgroundColor": "WindowText",
}, },
}, },
}, },
Object { Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"color": "WindowText", "color": "WindowText",
}, },
@@ -1743,7 +1728,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1752,7 +1737,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
}, },
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1760,7 +1745,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid GrayText", "border": "1px solid GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1782,16 +1767,14 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -1813,7 +1796,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
"splitButtonMenuIconDisabled": Object { "splitButtonMenuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -2103,7 +2086,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
> >
<button <button
aria-label="OK" aria-label="OK"
className="ms-Button ms-Button--primary root-122" className="ms-Button ms-Button--primary root-66"
data-is-focusable={true} data-is-focusable={true}
id="sidePanelOkButton" id="sidePanelOkButton"
onClick={[Function]} onClick={[Function]}
@@ -2115,16 +2098,16 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
type="submit" type="submit"
> >
<span <span
className="ms-Button-flexContainer flexContainer-123" className="ms-Button-flexContainer flexContainer-67"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<span <span
className="ms-Button-textContainer textContainer-124" className="ms-Button-textContainer textContainer-68"
> >
<span <span
className="ms-Button-label label-126" className="ms-Button-label label-70"
id="id__5" id="id__3"
key="id__5" key="id__3"
> >
OK OK
</span> </span>

View File

@@ -306,7 +306,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<label <label
className="ms-Label root-109" className="ms-Label root-53"
> >
Partition key value Partition key value
</label> </label>
@@ -316,11 +316,11 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
horizontal={true} horizontal={true}
> >
<div <div
className="ms-Stack css-110" className="ms-Stack css-54"
> >
<Dropdown <Dropdown
defaultSelectedKey="string" defaultSelectedKey="string"
key=".0:$.$.0" key=".0:$.0"
label="Key" label="Key"
onChange={[Function]} onChange={[Function]}
options={ options={
@@ -661,7 +661,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
] ]
} }
responsiveMode={0} responsiveMode={3}
styles={[Function]} styles={[Function]}
tabIndex={0} tabIndex={0}
theme={ theme={
@@ -1225,7 +1225,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<label <label
className="ms-Label ms-Dropdown-label root-129" className="ms-Label ms-Dropdown-label root-71"
id="Dropdown0-label" id="Dropdown0-label"
> >
Key Key
@@ -1236,7 +1236,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
aria-expanded="false" aria-expanded="false"
aria-haspopup="listbox" aria-haspopup="listbox"
aria-labelledby="Dropdown0-label Dropdown0-option" aria-labelledby="Dropdown0-label Dropdown0-option"
className="ms-Dropdown dropdown-111" className="ms-Dropdown dropdown-55"
data-is-focusable={true} data-is-focusable={true}
data-ktp-target={true} data-ktp-target={true}
id="Dropdown0" id="Dropdown0"
@@ -1251,23 +1251,25 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
tabIndex={0} tabIndex={0}
> >
<span <span
aria-atomic={true}
aria-invalid={false} aria-invalid={false}
className="ms-Dropdown-title title-156" aria-live="polite"
className="ms-Dropdown-title title-97"
id="Dropdown0-option" id="Dropdown0-option"
> >
String String
</span> </span>
<span <span
className="ms-Dropdown-caretDownWrapper caretDownWrapper-113" className="ms-Dropdown-caretDownWrapper caretDownWrapper-57"
> >
<StyledIconBase <StyledIconBase
aria-hidden={true} aria-hidden={true}
className="ms-Dropdown-caretDown caretDown-114" className="ms-Dropdown-caretDown caretDown-58"
iconName="ChevronDown" iconName="ChevronDown"
> >
<IconBase <IconBase
aria-hidden={true} aria-hidden={true}
className="ms-Dropdown-caretDown caretDown-114" className="ms-Dropdown-caretDown caretDown-58"
iconName="ChevronDown" iconName="ChevronDown"
styles={[Function]} styles={[Function]}
theme={ theme={
@@ -1546,7 +1548,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<i <i
aria-hidden={true} aria-hidden={true}
className="ms-Dropdown-caretDown caretDown-132" className="ms-Dropdown-caretDown caretDown-73"
data-icon-name="ChevronDown" data-icon-name="ChevronDown"
> >
@@ -1561,7 +1563,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</Dropdown> </Dropdown>
<StyledTextFieldBase <StyledTextFieldBase
id="confirmCollectionId" id="confirmCollectionId"
key=".0:$.$.1" key=".0:$.1"
label="Value" label="Value"
onChange={[Function]} onChange={[Function]}
tabIndex={0} tabIndex={0}
@@ -1850,7 +1852,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
validateOnLoad={true} validateOnLoad={true}
> >
<div <div
className="ms-TextField root-134" className="ms-TextField root-75"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
@@ -2139,7 +2141,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<label <label
className="ms-Label root-109" className="ms-Label root-53"
htmlFor="confirmCollectionId" htmlFor="confirmCollectionId"
id="TextFieldLabel3" id="TextFieldLabel3"
> >
@@ -2148,12 +2150,12 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</LabelBase> </LabelBase>
</StyledLabelBase> </StyledLabelBase>
<div <div
className="ms-TextField-fieldGroup fieldGroup-135" className="ms-TextField-fieldGroup fieldGroup-76"
> >
<input <input
aria-invalid={false} aria-invalid={false}
aria-labelledby="TextFieldLabel3" aria-labelledby="TextFieldLabel3"
className="ms-TextField-field field-136" className="ms-TextField-field field-77"
id="confirmCollectionId" id="confirmCollectionId"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@@ -2462,7 +2464,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<label <label
className="ms-Label root-109" className="ms-Label root-53"
> >
Enter input parameters (if any) Enter input parameters (if any)
</label> </label>
@@ -2472,11 +2474,11 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
horizontal={true} horizontal={true}
> >
<div <div
className="ms-Stack css-110" className="ms-Stack css-54"
> >
<Dropdown <Dropdown
defaultSelectedKey="string" defaultSelectedKey="string"
key=".0:$.$.0" key=".0:$.0"
label="Key" label="Key"
onChange={[Function]} onChange={[Function]}
options={ options={
@@ -2817,7 +2819,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
] ]
} }
responsiveMode={0} responsiveMode={3}
styles={[Function]} styles={[Function]}
tabIndex={0} tabIndex={0}
theme={ theme={
@@ -3099,12 +3101,12 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<StyledLabelBase <StyledLabelBase
className="ms-Dropdown-label" className="ms-Dropdown-label"
id="Dropdown6-label" id="Dropdown4-label"
styles={[Function]} styles={[Function]}
> >
<LabelBase <LabelBase
className="ms-Dropdown-label" className="ms-Dropdown-label"
id="Dropdown6-label" id="Dropdown4-label"
styles={[Function]} styles={[Function]}
theme={ theme={
Object { Object {
@@ -3381,8 +3383,8 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<label <label
className="ms-Label ms-Dropdown-label root-129" className="ms-Label ms-Dropdown-label root-71"
id="Dropdown6-label" id="Dropdown4-label"
> >
Key Key
</label> </label>
@@ -3391,11 +3393,11 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
<div <div
aria-expanded="false" aria-expanded="false"
aria-haspopup="listbox" aria-haspopup="listbox"
aria-labelledby="Dropdown6-label Dropdown6-option" aria-labelledby="Dropdown4-label Dropdown4-option"
className="ms-Dropdown dropdown-111" className="ms-Dropdown dropdown-55"
data-is-focusable={true} data-is-focusable={true}
data-ktp-target={true} data-ktp-target={true}
id="Dropdown6" id="Dropdown4"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
onClick={[Function]} onClick={[Function]}
@@ -3407,23 +3409,25 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
tabIndex={0} tabIndex={0}
> >
<span <span
aria-atomic={true}
aria-invalid={false} aria-invalid={false}
className="ms-Dropdown-title title-156" aria-live="polite"
id="Dropdown6-option" className="ms-Dropdown-title title-97"
id="Dropdown4-option"
> >
String String
</span> </span>
<span <span
className="ms-Dropdown-caretDownWrapper caretDownWrapper-113" className="ms-Dropdown-caretDownWrapper caretDownWrapper-57"
> >
<StyledIconBase <StyledIconBase
aria-hidden={true} aria-hidden={true}
className="ms-Dropdown-caretDown caretDown-114" className="ms-Dropdown-caretDown caretDown-58"
iconName="ChevronDown" iconName="ChevronDown"
> >
<IconBase <IconBase
aria-hidden={true} aria-hidden={true}
className="ms-Dropdown-caretDown caretDown-114" className="ms-Dropdown-caretDown caretDown-58"
iconName="ChevronDown" iconName="ChevronDown"
styles={[Function]} styles={[Function]}
theme={ theme={
@@ -3702,7 +3706,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<i <i
aria-hidden={true} aria-hidden={true}
className="ms-Dropdown-caretDown caretDown-132" className="ms-Dropdown-caretDown caretDown-73"
data-icon-name="ChevronDown" data-icon-name="ChevronDown"
> >
@@ -3718,7 +3722,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
<StyledTextFieldBase <StyledTextFieldBase
defaultValue="" defaultValue=""
id="confirmCollectionId" id="confirmCollectionId"
key=".0:$.$.1" key=".0:$.1"
label="Param" label="Param"
onChange={[Function]} onChange={[Function]}
tabIndex={0} tabIndex={0}
@@ -4008,19 +4012,19 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
validateOnLoad={true} validateOnLoad={true}
> >
<div <div
className="ms-TextField root-134" className="ms-TextField root-75"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<StyledLabelBase <StyledLabelBase
htmlFor="confirmCollectionId" htmlFor="confirmCollectionId"
id="TextFieldLabel9" id="TextFieldLabel7"
styles={[Function]} styles={[Function]}
> >
<LabelBase <LabelBase
htmlFor="confirmCollectionId" htmlFor="confirmCollectionId"
id="TextFieldLabel9" id="TextFieldLabel7"
styles={[Function]} styles={[Function]}
theme={ theme={
Object { Object {
@@ -4297,21 +4301,21 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
} }
> >
<label <label
className="ms-Label root-109" className="ms-Label root-53"
htmlFor="confirmCollectionId" htmlFor="confirmCollectionId"
id="TextFieldLabel9" id="TextFieldLabel7"
> >
Param Param
</label> </label>
</LabelBase> </LabelBase>
</StyledLabelBase> </StyledLabelBase>
<div <div
className="ms-TextField-fieldGroup fieldGroup-135" className="ms-TextField-fieldGroup fieldGroup-76"
> >
<input <input
aria-invalid={false} aria-invalid={false}
aria-labelledby="TextFieldLabel9" aria-labelledby="TextFieldLabel7"
className="ms-TextField-field field-136" className="ms-TextField-field field-77"
id="confirmCollectionId" id="confirmCollectionId"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@@ -4327,7 +4331,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</TextFieldBase> </TextFieldBase>
</StyledTextFieldBase> </StyledTextFieldBase>
<div <div
key=".0:$.$.2/.$.$.0"
tabIndex={0} tabIndex={0}
> >
<StyledImageBase <StyledImageBase
@@ -4625,7 +4628,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
width={20} width={20}
> >
<div <div
className="ms-Image addRemoveIconLabel root-145" className="ms-Image addRemoveIconLabel root-86"
style={ style={
Object { Object {
"height": 30, "height": 30,
@@ -4635,7 +4638,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<img <img
alt="Delete param" alt="Delete param"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-146" className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-87"
id="deleteparam" id="deleteparam"
key="fabricImage[object Object]" key="fabricImage[object Object]"
onClick={[Function]} onClick={[Function]}
@@ -4649,7 +4652,6 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</StyledImageBase> </StyledImageBase>
</div> </div>
<div <div
key=".0:$.$.2/.$.$.1"
tabIndex={0} tabIndex={0}
> >
<StyledImageBase <StyledImageBase
@@ -4947,7 +4949,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
width={20} width={20}
> >
<div <div
className="ms-Image addRemoveIconLabel root-145" className="ms-Image addRemoveIconLabel root-86"
style={ style={
Object { Object {
"height": 30, "height": 30,
@@ -4957,7 +4959,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<img <img
alt="Add param" alt="Add param"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-146" className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-87"
id="addparam" id="addparam"
key="fabricImage[object Object]" key="fabricImage[object Object]"
onClick={[Function]} onClick={[Function]}
@@ -4979,14 +4981,14 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
tabIndex={0} tabIndex={0}
> >
<div <div
className="ms-Stack css-110" className="ms-Stack css-54"
onClick={[Function]} onClick={[Function]}
tabIndex={0} tabIndex={0}
> >
<StyledImageBase <StyledImageBase
alt="Add param" alt="Add param"
height={30} height={30}
key=".0:$.$.0" key=".0:$.0"
src={Object {}} src={Object {}}
width={20} width={20}
> >
@@ -5271,7 +5273,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
width={20} width={20}
> >
<div <div
className="ms-Image root-145" className="ms-Image root-86"
style={ style={
Object { Object {
"height": 30, "height": 30,
@@ -5281,7 +5283,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<img <img
alt="Add param" alt="Add param"
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-146" className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-87"
key="fabricImage[object Object]" key="fabricImage[object Object]"
onError={[Function]} onError={[Function]}
onLoad={[Function]} onLoad={[Function]}
@@ -5292,10 +5294,10 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
</StyledImageBase> </StyledImageBase>
<Text <Text
className="addNewParamStyle" className="addNewParamStyle"
key=".0:$.$.1" key=".0:$.1"
> >
<span <span
className="addNewParamStyle css-147" className="addNewParamStyle css-88"
> >
Add New Param Add New Param
</span> </span>
@@ -6192,7 +6194,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -6218,7 +6220,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -6230,16 +6232,14 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -6270,7 +6270,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"display": "inline-block", "display": "inline-block",
"padding": "0 16px", "padding": "0 16px",
"selectors": Object { "selectors": Object {
":active > span": Object { ":active > *": Object {
"left": 0, "left": 0,
"position": "relative", "position": "relative",
"top": 0, "top": 0,
@@ -6297,7 +6297,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -6322,16 +6322,14 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -6365,7 +6363,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"backgroundColor": "#f3f2f1", "backgroundColor": "#f3f2f1",
"color": "#d2d0ce", "color": "#d2d0ce",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -6382,7 +6380,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"border": "1px solid #106ebe", "border": "1px solid #106ebe",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"borderColor": "Highlight", "borderColor": "Highlight",
"color": "Window", "color": "Window",
@@ -6394,7 +6392,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"border": "1px solid #005a9e", "border": "1px solid #005a9e",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -6410,13 +6408,12 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"overflow": "hidden", "overflow": "hidden",
"padding": 0, "padding": 0,
"position": "absolute", "position": "absolute",
"whiteSpace": "nowrap",
"width": 1, "width": 1,
}, },
"splitButtonContainer": Array [ "splitButtonContainer": Array [
Object { Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
}, },
}, },
@@ -6427,16 +6424,14 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": "none",
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -6459,21 +6454,13 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderRight": "none", "borderRight": "none",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
}, },
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"border": "none", "border": "none",
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
"selectors": Object { "selectors": Object {
":active": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none",
},
":hover": Object {
"border": "none",
},
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid WindowText", "border": "1px solid WindowText",
@@ -6486,7 +6473,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderLeftWidth": "0", "borderLeftWidth": "0",
}, },
@@ -6499,7 +6486,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -6513,7 +6500,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -6527,7 +6514,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"border": "none", "border": "none",
"outline": "none", "outline": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
@@ -6543,7 +6530,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"color": "Window", "color": "Window",
}, },
@@ -6552,7 +6539,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
".ms-Button.is-disabled": Object { ".ms-Button.is-disabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -6568,7 +6555,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
}, },
}, },
@@ -6580,7 +6567,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@@ -6593,7 +6580,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "GrayText", "backgroundColor": "GrayText",
}, },
}, },
@@ -6615,18 +6602,18 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
":hover": Object { ":hover": Object {
"backgroundColor": "#106ebe", "backgroundColor": "#106ebe",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "Highlight", "color": "Highlight",
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Canvas", "backgroundColor": "WindowText",
}, },
}, },
}, },
Object { Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"color": "WindowText", "color": "WindowText",
}, },
@@ -6676,7 +6663,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -6685,7 +6672,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
}, },
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -6693,7 +6680,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid GrayText", "border": "1px solid GrayText",
"color": "GrayText", "color": "GrayText",
@@ -6715,16 +6702,14 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -6746,7 +6731,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
"splitButtonMenuIconDisabled": Object { "splitButtonMenuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -7036,7 +7021,7 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
> >
<button <button
aria-label="Execute" aria-label="Execute"
className="ms-Button ms-Button--primary root-148" className="ms-Button ms-Button--primary root-89"
data-is-focusable={true} data-is-focusable={true}
id="sidePanelOkButton" id="sidePanelOkButton"
onClick={[Function]} onClick={[Function]}
@@ -7048,16 +7033,16 @@ exports[`Excute Sproc Param Pane should render Default properly 1`] = `
type="submit" type="submit"
> >
<span <span
className="ms-Button-flexContainer flexContainer-149" className="ms-Button-flexContainer flexContainer-90"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<span <span
className="ms-Button-textContainer textContainer-150" className="ms-Button-textContainer textContainer-91"
> >
<span <span
className="ms-Button-label label-152" className="ms-Button-label label-93"
id="id__12" id="id__8"
key="id__12" key="id__8"
> >
Execute Execute
</span> </span>

View File

@@ -112,9 +112,8 @@
margin-top: 28px; margin-top: 28px;
margin-left: 4px !important; margin-left: 4px !important;
} }
.addRemoveIcon [alt="editEntity"]:focus, .addRemoveIcon [alt="editEntity"]:focus,.addRemoveIconLabel [alt="editEntity"]:focus{
.addRemoveIconLabel [alt="editEntity"]:focus { border:1px dashed #605E5C
border: 1px dashed #605e5c;
} }
.addNewParamStyle { .addNewParamStyle {
margin-top: 5px; margin-top: 5px;
@@ -154,9 +153,6 @@
.backImageIcon { .backImageIcon {
margin-top: 8px; margin-top: 8px;
} }
[alt="back"]:focus {
border: 1px solid #605e5c;
}
.addEntityDatePicker { .addEntityDatePicker {
max-width: 145px; max-width: 145px;
} }

View File

@@ -29,7 +29,7 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
}; };
} }
componentDidMount(): void { omponentDidMount(): void {
window.addEventListener("resize", () => this.setState({ height: this.getPanelHeight() })); window.addEventListener("resize", () => this.setState({ height: this.getPanelHeight() }));
} }
@@ -62,12 +62,12 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
customWidth={this.props.panelWidth ? this.props.panelWidth : "440px"} customWidth={this.props.panelWidth ? this.props.panelWidth : "440px"}
headerClassName="panelHeader" headerClassName="panelHeader"
onRenderNavigationContent={this.props.onRenderNavigationContent} onRenderNavigationContent={this.props.onRenderNavigationContent}
isFooterAtBottom={true}
styles={{ styles={{
navigation: { borderBottom: "1px solid #cccccc" }, navigation: { borderBottom: "1px solid #cccccc" },
content: { padding: 0 }, content: { padding: 0, height: "100%" },
scrollableContent: { height: "100%" },
header: { padding: "0 0 8px 34px" }, header: { padding: "0 0 8px 34px" },
commands: { marginTop: 8, paddingTop: 0 }, commands: { marginTop: 8 },
}} }}
style={{ height: this.state.height }} style={{ height: this.state.height }}
> >

View File

@@ -32,8 +32,14 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
return ( return (
<Stack className="panelInfoErrorContainer" horizontal verticalAlign="center"> <Stack className="panelInfoErrorContainer" horizontal verticalAlign="center">
{icon} {icon}
<span className="panelWarningErrorDetailsLinkContainer" role="alert" aria-live="assertive"> <span className="panelWarningErrorDetailsLinkContainer">
<Text aria-label={message} className="panelWarningErrorMessage" variant="small"> <Text
role="alert"
aria-live="assertive"
aria-label={message}
className="panelWarningErrorMessage"
variant="small"
>
{message} {message}
{link && linkText && ( {link && linkText && (
<Link target="_blank" href={link}> <Link target="_blank" href={link}>

View File

@@ -1,5 +1,5 @@
import { fireEvent, render, screen } from "@testing-library/react"; import { fireEvent, render, screen } from "@testing-library/react";
import { ReactWrapper, mount } from "enzyme"; import { mount, ReactWrapper } from "enzyme";
import React from "react"; import React from "react";
import { RightPaneForm } from "./RightPaneForm"; import { RightPaneForm } from "./RightPaneForm";
@@ -34,6 +34,6 @@ describe("Right Pane Form", () => {
it("should render error in header", () => { it("should render error in header", () => {
render(<RightPaneForm {...props} formError="file already Exist" />); render(<RightPaneForm {...props} formError="file already Exist" />);
expect(screen.getByLabelText("error")).toBeDefined(); expect(screen.getByLabelText("error")).toBeDefined();
expect(screen.getByRole("alert").innerHTML).toContain("file already Exist"); expect(screen.getByRole("alert").innerHTML).toEqual("file already Exist");
}); });
}); });

View File

@@ -901,7 +901,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -927,7 +927,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -939,16 +939,14 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -979,7 +977,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"display": "inline-block", "display": "inline-block",
"padding": "0 16px", "padding": "0 16px",
"selectors": Object { "selectors": Object {
":active > span": Object { ":active > *": Object {
"left": 0, "left": 0,
"position": "relative", "position": "relative",
"top": 0, "top": 0,
@@ -1006,7 +1004,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -1031,16 +1029,14 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1074,7 +1070,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"backgroundColor": "#f3f2f1", "backgroundColor": "#f3f2f1",
"color": "#d2d0ce", "color": "#d2d0ce",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1091,7 +1087,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"border": "1px solid #106ebe", "border": "1px solid #106ebe",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"borderColor": "Highlight", "borderColor": "Highlight",
"color": "Window", "color": "Window",
@@ -1103,7 +1099,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"border": "1px solid #005a9e", "border": "1px solid #005a9e",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -1119,13 +1115,12 @@ exports[`Right Pane Form should render Default properly 1`] = `
"overflow": "hidden", "overflow": "hidden",
"padding": 0, "padding": 0,
"position": "absolute", "position": "absolute",
"whiteSpace": "nowrap",
"width": 1, "width": 1,
}, },
"splitButtonContainer": Array [ "splitButtonContainer": Array [
Object { Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
}, },
}, },
@@ -1136,16 +1131,14 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": "none",
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -1168,21 +1161,13 @@ exports[`Right Pane Form should render Default properly 1`] = `
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderRight": "none", "borderRight": "none",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
}, },
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"border": "none", "border": "none",
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
"selectors": Object { "selectors": Object {
":active": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none",
},
":hover": Object {
"border": "none",
},
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid WindowText", "border": "1px solid WindowText",
@@ -1195,7 +1180,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderLeftWidth": "0", "borderLeftWidth": "0",
}, },
@@ -1208,7 +1193,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -1222,7 +1207,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -1236,7 +1221,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"border": "none", "border": "none",
"outline": "none", "outline": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
@@ -1252,7 +1237,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"color": "Window", "color": "Window",
}, },
@@ -1261,7 +1246,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
".ms-Button.is-disabled": Object { ".ms-Button.is-disabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1277,7 +1262,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
}, },
}, },
@@ -1289,7 +1274,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@@ -1302,7 +1287,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "GrayText", "backgroundColor": "GrayText",
}, },
}, },
@@ -1324,18 +1309,18 @@ exports[`Right Pane Form should render Default properly 1`] = `
":hover": Object { ":hover": Object {
"backgroundColor": "#106ebe", "backgroundColor": "#106ebe",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "Highlight", "color": "Highlight",
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Canvas", "backgroundColor": "WindowText",
}, },
}, },
}, },
Object { Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"color": "WindowText", "color": "WindowText",
}, },
@@ -1385,7 +1370,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1394,7 +1379,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
}, },
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1402,7 +1387,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid GrayText", "border": "1px solid GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1424,16 +1409,14 @@ exports[`Right Pane Form should render Default properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -1455,7 +1438,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
"splitButtonMenuIconDisabled": Object { "splitButtonMenuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1745,7 +1728,7 @@ exports[`Right Pane Form should render Default properly 1`] = `
> >
<button <button
aria-label="Load" aria-label="Load"
className="ms-Button ms-Button--primary root-109" className="ms-Button ms-Button--primary root-53"
data-is-focusable={true} data-is-focusable={true}
id="sidePanelOkButton" id="sidePanelOkButton"
onClick={[Function]} onClick={[Function]}
@@ -1757,14 +1740,14 @@ exports[`Right Pane Form should render Default properly 1`] = `
type="submit" type="submit"
> >
<span <span
className="ms-Button-flexContainer flexContainer-110" className="ms-Button-flexContainer flexContainer-54"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<span <span
className="ms-Button-textContainer textContainer-111" className="ms-Button-textContainer textContainer-55"
> >
<span <span
className="ms-Button-label label-113" className="ms-Button-label label-57"
id="id__0" id="id__0"
key="id__0" key="id__0"
> >

View File

@@ -6,7 +6,7 @@ import { SettingsPane } from "./SettingsPane";
describe("Settings Pane", () => { describe("Settings Pane", () => {
it("should render Default properly", () => { it("should render Default properly", () => {
const wrapper = shallow(<SettingsPane explorer={null} />); const wrapper = shallow(<SettingsPane />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
@@ -18,7 +18,7 @@ describe("Settings Pane", () => {
}, },
} as DatabaseAccount, } as DatabaseAccount,
}); });
const wrapper = shallow(<SettingsPane explorer={null} />); const wrapper = shallow(<SettingsPane />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
}); });

View File

@@ -1,55 +1,24 @@
import { import { Checkbox, ChoiceGroup, IChoiceGroupOption, SpinButton } from "@fluentui/react";
Checkbox,
ChoiceGroup,
IChoiceGroupOption,
ISpinButtonStyles,
IToggleStyles,
Position,
SpinButton,
Toggle,
} from "@fluentui/react";
import * as Constants from "Common/Constants"; import * as Constants from "Common/Constants";
import { InfoTooltip } from "Common/Tooltip/InfoTooltip"; import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
import { configContext } from "ConfigContext"; import { configContext } from "ConfigContext";
import { import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
DefaultRUThreshold,
LocalStorageUtility,
StorageKey,
getRUThreshold,
ruThresholdEnabled as isRUThresholdEnabled,
} from "Shared/StorageUtility";
import * as StringUtility from "Shared/StringUtility"; import * as StringUtility from "Shared/StringUtility";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { logConsoleInfo } from "Utils/NotificationConsoleUtils"; import { logConsoleInfo } from "Utils/NotificationConsoleUtils";
import * as PriorityBasedExecutionUtils from "Utils/PriorityBasedExecutionUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, MouseEvent, useState } from "react";
import Explorer from "../../Explorer";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
import * as PriorityBasedExecutionUtils from "Utils/PriorityBasedExecutionUtils";
export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ export const SettingsPane: FunctionComponent = () => {
explorer,
}: {
explorer: Explorer;
}): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [isExecuting, setIsExecuting] = useState<boolean>(false); const [isExecuting, setIsExecuting] = useState<boolean>(false);
const [refreshExplorer, setRefreshExplorer] = useState<boolean>(false);
const [pageOption, setPageOption] = useState<string>( const [pageOption, setPageOption] = useState<string>(
LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage) === Constants.Queries.unlimitedItemsPerPage LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage) === Constants.Queries.unlimitedItemsPerPage
? Constants.Queries.UnlimitedPageOption ? Constants.Queries.UnlimitedPageOption
: Constants.Queries.CustomPageOption, : Constants.Queries.CustomPageOption,
); );
const [ruThresholdEnabled, setRUThresholdEnabled] = useState<boolean>(isRUThresholdEnabled());
const [ruThreshold, setRUThreshold] = useState<number>(getRUThreshold());
const [queryTimeoutEnabled, setQueryTimeoutEnabled] = useState<boolean>(
LocalStorageUtility.getEntryBoolean(StorageKey.QueryTimeoutEnabled),
);
const [queryTimeout, setQueryTimeout] = useState<number>(LocalStorageUtility.getEntryNumber(StorageKey.QueryTimeout));
const [automaticallyCancelQueryAfterTimeout, setAutomaticallyCancelQueryAfterTimeout] = useState<boolean>(
LocalStorageUtility.getEntryBoolean(StorageKey.AutomaticallyCancelQueryAfterTimeout),
);
const [customItemPerPage, setCustomItemPerPage] = useState<number>( const [customItemPerPage, setCustomItemPerPage] = useState<number>(
LocalStorageUtility.getEntryNumber(StorageKey.CustomItemPerPage) || 0, LocalStorageUtility.getEntryNumber(StorageKey.CustomItemPerPage) || 0,
); );
@@ -68,21 +37,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
? LocalStorageUtility.getEntryString(StorageKey.IsGraphAutoVizDisabled) ? LocalStorageUtility.getEntryString(StorageKey.IsGraphAutoVizDisabled)
: "false", : "false",
); );
const [retryAttempts, setRetryAttempts] = useState<number>(
LocalStorageUtility.hasItem(StorageKey.RetryAttempts)
? LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts)
: Constants.Queries.DefaultRetryAttempts,
);
const [retryInterval, setRetryInterval] = useState<number>(
LocalStorageUtility.hasItem(StorageKey.RetryInterval)
? LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval)
: Constants.Queries.DefaultRetryIntervalInMs,
);
const [MaxWaitTimeInSeconds, setMaxWaitTimeInSeconds] = useState<number>(
LocalStorageUtility.hasItem(StorageKey.MaxWaitTimeInSeconds)
? LocalStorageUtility.getEntryNumber(StorageKey.MaxWaitTimeInSeconds)
: Constants.Queries.DefaultMaxWaitTimeInSeconds,
);
const [maxDegreeOfParallelism, setMaxDegreeOfParallelism] = useState<number>( const [maxDegreeOfParallelism, setMaxDegreeOfParallelism] = useState<number>(
LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism) LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism)
? LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism) ? LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism)
@@ -93,17 +47,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
? LocalStorageUtility.getEntryString(StorageKey.PriorityLevel) ? LocalStorageUtility.getEntryString(StorageKey.PriorityLevel)
: Constants.PriorityLevel.Default, : Constants.PriorityLevel.Default,
); );
const [copilotSampleDBEnabled, setCopilotSampleDBEnabled] = useState<boolean>(
LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true",
);
const explorerVersion = configContext.gitSha; const explorerVersion = configContext.gitSha;
const shouldShowQueryPageOptions = userContext.apiType === "SQL"; const shouldShowQueryPageOptions = userContext.apiType === "SQL";
const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin"; const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin";
const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin"; const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin";
const shouldShowParallelismOption = userContext.apiType !== "Gremlin"; const shouldShowParallelismOption = userContext.apiType !== "Gremlin";
const shouldShowPriorityLevelOption = PriorityBasedExecutionUtils.isFeatureEnabled(); const shouldShowPriorityLevelOption = PriorityBasedExecutionUtils.isFeatureEnabled();
const shouldShowCopilotSampleDBOption = userContext.apiType === "SQL" && useQueryCopilot.getState().copilotEnabled; const handlerOnSubmit = (e: MouseEvent<HTMLButtonElement>) => {
const handlerOnSubmit = async () => {
setIsExecuting(true); setIsExecuting(true);
LocalStorageUtility.setEntryNumber( LocalStorageUtility.setEntryNumber(
@@ -111,16 +61,10 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
isCustomPageOptionSelected() ? customItemPerPage : Constants.Queries.unlimitedItemsPerPage, isCustomPageOptionSelected() ? customItemPerPage : Constants.Queries.unlimitedItemsPerPage,
); );
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage); LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled);
LocalStorageUtility.setEntryBoolean(StorageKey.QueryTimeoutEnabled, queryTimeoutEnabled);
LocalStorageUtility.setEntryNumber(StorageKey.RetryAttempts, retryAttempts);
LocalStorageUtility.setEntryNumber(StorageKey.RetryInterval, retryInterval);
LocalStorageUtility.setEntryNumber(StorageKey.MaxWaitTimeInSeconds, MaxWaitTimeInSeconds);
LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString()); LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString());
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString()); LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString());
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism); LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism);
LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString()); LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString());
LocalStorageUtility.setEntryString(StorageKey.CopilotSampleDBEnabled, copilotSampleDBEnabled.toString());
if (shouldShowGraphAutoVizOption) { if (shouldShowGraphAutoVizOption) {
LocalStorageUtility.setEntryBoolean( LocalStorageUtility.setEntryBoolean(
@@ -129,18 +73,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
); );
} }
if (ruThresholdEnabled) {
LocalStorageUtility.setEntryNumber(StorageKey.RUThreshold, ruThreshold);
}
if (queryTimeoutEnabled) {
LocalStorageUtility.setEntryNumber(StorageKey.QueryTimeout, queryTimeout);
LocalStorageUtility.setEntryBoolean(
StorageKey.AutomaticallyCancelQueryAfterTimeout,
automaticallyCancelQueryAfterTimeout,
);
}
setIsExecuting(false); setIsExecuting(false);
logConsoleInfo( logConsoleInfo(
`Updated items per page setting to ${LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage)}`, `Updated items per page setting to ${LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage)}`,
@@ -164,8 +96,8 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
logConsoleInfo( logConsoleInfo(
`Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`, `Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`,
); );
refreshExplorer && (await explorer.refreshExplorer());
closeSidePanel(); closeSidePanel();
e.preventDefault();
}; };
const isCustomPageOptionSelected = () => { const isCustomPageOptionSelected = () => {
@@ -180,7 +112,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
formError: "", formError: "",
isExecuting, isExecuting,
submitButtonText: "Apply", submitButtonText: "Apply",
onSubmit: () => handlerOnSubmit(), onSubmit: () => handlerOnSubmit(undefined),
}; };
const pageOptionList: IChoiceGroupOption[] = [ const pageOptionList: IChoiceGroupOption[] = [
{ key: Constants.Queries.CustomPageOption, text: "Custom" }, { key: Constants.Queries.CustomPageOption, text: "Custom" },
@@ -208,59 +140,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
setPageOption(option.key); setPageOption(option.key);
}; };
const handleOnRUThresholdToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
setRUThresholdEnabled(checked);
};
const handleOnRUThresholdSpinButtonChange = (ev: React.MouseEvent<HTMLElement>, newValue?: string): void => {
const ruThreshold = Number(newValue);
if (!isNaN(ruThreshold)) {
setRUThreshold(ruThreshold);
}
};
const handleOnQueryTimeoutToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
setQueryTimeoutEnabled(checked);
};
const handleOnAutomaticallyCancelQueryToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
setAutomaticallyCancelQueryAfterTimeout(checked);
};
const handleOnQueryTimeoutSpinButtonChange = (ev: React.MouseEvent<HTMLElement>, newValue?: string): void => {
const queryTimeout = Number(newValue);
if (!isNaN(queryTimeout)) {
setQueryTimeout(queryTimeout);
}
};
const handleOnQueryRetryAttemptsSpinButtonChange = (ev: React.MouseEvent<HTMLElement>, newValue?: string): void => {
const retryAttempts = Number(newValue);
if (!isNaN(retryAttempts)) {
setRetryAttempts(retryAttempts);
}
};
const handleOnRetryIntervalSpinButtonChange = (ev: React.MouseEvent<HTMLElement>, newValue?: string): void => {
const retryInterval = Number(newValue);
if (!isNaN(retryInterval)) {
setRetryInterval(retryInterval);
}
};
const handleOnMaxWaitTimeSpinButtonChange = (ev: React.MouseEvent<HTMLElement>, newValue?: string): void => {
const MaxWaitTimeInSeconds = Number(newValue);
if (!isNaN(MaxWaitTimeInSeconds)) {
setMaxWaitTimeInSeconds(MaxWaitTimeInSeconds);
}
};
const handleSampleDatabaseChange = async (ev: React.MouseEvent<HTMLElement>, checked?: boolean): Promise<void> => {
setCopilotSampleDBEnabled(checked);
useQueryCopilot.getState().setCopilotSampleDBEnabled(checked);
setRefreshExplorer(false);
};
const choiceButtonStyles = { const choiceButtonStyles = {
root: { root: {
clear: "both", clear: "both",
@@ -282,35 +161,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
}, },
], ],
}; };
const toggleStyles: IToggleStyles = {
label: {
fontSize: 12,
fontWeight: 400,
display: "block",
},
root: {},
container: {},
pill: {},
thumb: {},
text: {},
};
const spinButtonStyles: ISpinButtonStyles = {
label: {
fontSize: 12,
fontWeight: 400,
},
root: {
paddingBottom: 10,
},
labelWrapper: {},
icon: {},
spinButtonWrapper: {},
input: {},
arrowButtonsContainer: {},
};
return ( return (
<RightPaneForm {...genericPaneProps}> <RightPaneForm {...genericPaneProps}>
<div className="paneMainContent"> <div className="paneMainContent">
@@ -361,158 +211,8 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div> </div>
</div> </div>
)} )}
{userContext.apiType === "SQL" && (
<>
<div className="settingsSection">
<div className="settingsSectionPart">
<div>
<legend id="ruThresholdLabel" className="settingsSectionLabel legendLabel">
RU Threshold
</legend>
<InfoTooltip>If a query exceeds a configured RU threshold, the query will be aborted.</InfoTooltip>
</div>
<div>
<Toggle
styles={toggleStyles}
label="Enable RU threshold"
onChange={handleOnRUThresholdToggleChange}
defaultChecked={ruThresholdEnabled}
/>
</div>
{ruThresholdEnabled && (
<div>
<SpinButton
label="RU Threshold (RU)"
labelPosition={Position.top}
defaultValue={(ruThreshold || DefaultRUThreshold).toString()}
min={1}
step={1000}
onChange={handleOnRUThresholdSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
styles={spinButtonStyles}
/>
</div>
)}
</div>
</div>
<div className="settingsSection">
<div className="settingsSectionPart">
<div>
<legend id="queryTimeoutLabel" className="settingsSectionLabel legendLabel">
Query Timeout
</legend>
<InfoTooltip>
When a query reaches a specified time limit, a popup with an option to cancel the query will show
unless automatic cancellation has been enabled
</InfoTooltip>
</div>
<div>
<Toggle
styles={toggleStyles}
label="Enable query timeout"
onChange={handleOnQueryTimeoutToggleChange}
defaultChecked={queryTimeoutEnabled}
/>
</div>
{queryTimeoutEnabled && (
<div>
<SpinButton
label="Query timeout (ms)"
labelPosition={Position.top}
defaultValue={(queryTimeout || 5000).toString()}
min={100}
step={1000}
onChange={handleOnQueryTimeoutSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
styles={spinButtonStyles}
/>
<Toggle
label="Automatically cancel query after timeout"
styles={toggleStyles}
onChange={handleOnAutomaticallyCancelQueryToggleChange}
defaultChecked={automaticallyCancelQueryAfterTimeout}
/>
</div>
)}
</div>
</div>
</>
)}
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart"> <div className="settingsSectionPart">
<div className="settingsSectionLabel">
Retry Settings
<InfoTooltip>Retry policy associated with throttled requests during CosmosDB queries.</InfoTooltip>
</div>
<div>
<legend id="queryRetryAttemptsLabel" className="settingsSectionLabel legendLabel">
Max retry attempts
</legend>
<InfoTooltip>Max number of retries to be performed for a request. Default value 9.</InfoTooltip>
</div>
<SpinButton
labelPosition={Position.top}
min={1}
step={1}
value={"" + retryAttempts}
onChange={handleOnQueryRetryAttemptsSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1"
decrementButtonAriaLabel="Decrease value by 1"
onIncrement={(newValue) => setRetryAttempts(parseInt(newValue) + 1 || retryAttempts)}
onDecrement={(newValue) => setRetryAttempts(parseInt(newValue) - 1 || retryAttempts)}
onValidate={(newValue) => setRetryAttempts(parseInt(newValue) || retryAttempts)}
styles={spinButtonStyles}
/>
<div>
<legend id="queryRetryAttemptsLabel" className="settingsSectionLabel legendLabel">
Fixed retry interval (ms)
</legend>
<InfoTooltip>
Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned as part
of the response. Default value is 0 milliseconds.
</InfoTooltip>
</div>
<SpinButton
labelPosition={Position.top}
min={1000}
step={1000}
value={"" + retryInterval}
onChange={handleOnRetryIntervalSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
onIncrement={(newValue) => setRetryInterval(parseInt(newValue) + 1000 || retryInterval)}
onDecrement={(newValue) => setRetryInterval(parseInt(newValue) - 1000 || retryInterval)}
onValidate={(newValue) => setRetryInterval(parseInt(newValue) || retryInterval)}
styles={spinButtonStyles}
/>
<div>
<legend id="queryRetryAttemptsLabel" className="settingsSectionLabel legendLabel">
Max wait time (s)
</legend>
<InfoTooltip>
Max wait time in seconds to wait for a request while the retries are happening. Default value 30
seconds.
</InfoTooltip>
</div>
<SpinButton
labelPosition={Position.top}
min={1}
step={1}
value={"" + MaxWaitTimeInSeconds}
onChange={handleOnMaxWaitTimeSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1"
decrementButtonAriaLabel="Decrease value by 1"
onIncrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) + 1 || MaxWaitTimeInSeconds)}
onDecrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) - 1 || MaxWaitTimeInSeconds)}
onValidate={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) || MaxWaitTimeInSeconds)}
styles={spinButtonStyles}
/>
</div>
</div>
<div className="settingsSection">
<div className="settingsSectionPart settingsSectionInlineCheckbox">
<div className="settingsSectionLabel"> <div className="settingsSectionLabel">
Enable container pagination Enable container pagination
<InfoTooltip> <InfoTooltip>
@@ -532,7 +232,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div> </div>
{shouldShowCrossPartitionOption && ( {shouldShowCrossPartitionOption && (
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart settingsSectionInlineCheckbox"> <div className="settingsSectionPart">
<div className="settingsSectionLabel"> <div className="settingsSectionLabel">
Enable cross-partition query Enable cross-partition query
<InfoTooltip> <InfoTooltip>
@@ -623,30 +323,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div> </div>
</div> </div>
)} )}
{shouldShowCopilotSampleDBOption && (
<div className="settingsSection">
<div className="settingsSectionPart settingsSectionInlineCheckbox">
<div className="settingsSectionLabel">
Enable sample database
<InfoTooltip>
This is a sample database and collection with synthetic product data you can use to explore using
NoSQL queries and Copilot. This will appear as another database in the Data Explorer UI, and is
created by, and maintained by Microsoft at no cost to you.
</InfoTooltip>
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="Enable sample db for Copilot"
checked={copilotSampleDBEnabled}
onChange={handleSampleDatabaseChange}
/>
</div>
</div>
)}
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart"> <div className="settingsSectionPart">
<div className="settingsSectionLabel">Explorer Version</div> <div className="settingsSectionLabel">Explorer Version</div>

View File

@@ -102,247 +102,6 @@ exports[`Settings Pane should render Default properly 1`] = `
> >
<div <div
className="settingsSectionPart" className="settingsSectionPart"
>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="ruThresholdLabel"
>
RU Threshold
</legend>
<InfoTooltip>
If a query exceeds a configured RU threshold, the query will be aborted.
</InfoTooltip>
</div>
<div>
<StyledToggleBase
defaultChecked={true}
label="Enable RU threshold"
onChange={[Function]}
styles={
Object {
"container": Object {},
"label": Object {
"display": "block",
"fontSize": 12,
"fontWeight": 400,
},
"pill": Object {},
"root": Object {},
"text": Object {},
"thumb": Object {},
}
}
/>
</div>
<div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1000"
defaultValue="5000"
incrementButtonAriaLabel="Increase value by 1000"
label="RU Threshold (RU)"
labelPosition={0}
min={1}
onChange={[Function]}
step={1000}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
/>
</div>
</div>
</div>
<div
className="settingsSection"
>
<div
className="settingsSectionPart"
>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryTimeoutLabel"
>
Query Timeout
</legend>
<InfoTooltip>
When a query reaches a specified time limit, a popup with an option to cancel the query will show unless automatic cancellation has been enabled
</InfoTooltip>
</div>
<div>
<StyledToggleBase
defaultChecked={false}
label="Enable query timeout"
onChange={[Function]}
styles={
Object {
"container": Object {},
"label": Object {
"display": "block",
"fontSize": 12,
"fontWeight": 400,
},
"pill": Object {},
"root": Object {},
"text": Object {},
"thumb": Object {},
}
}
/>
</div>
</div>
</div>
<div
className="settingsSection"
>
<div
className="settingsSectionPart"
>
<div
className="settingsSectionLabel"
>
Retry Settings
<InfoTooltip>
Retry policy associated with throttled requests during CosmosDB queries.
</InfoTooltip>
</div>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryRetryAttemptsLabel"
>
Max retry attempts
</legend>
<InfoTooltip>
Max number of retries to be performed for a request. Default value 9.
</InfoTooltip>
</div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1"
incrementButtonAriaLabel="Increase value by 1"
labelPosition={0}
min={1}
onChange={[Function]}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={1}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
value="9"
/>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryRetryAttemptsLabel"
>
Fixed retry interval (ms)
</legend>
<InfoTooltip>
Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned as part of the response. Default value is 0 milliseconds.
</InfoTooltip>
</div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1000"
incrementButtonAriaLabel="Increase value by 1000"
labelPosition={0}
min={1000}
onChange={[Function]}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={1000}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
value="0"
/>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryRetryAttemptsLabel"
>
Max wait time (s)
</legend>
<InfoTooltip>
Max wait time in seconds to wait for a request while the retries are happening. Default value 30 seconds.
</InfoTooltip>
</div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1"
incrementButtonAriaLabel="Increase value by 1"
labelPosition={0}
min={1}
onChange={[Function]}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={1}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
value="30"
/>
</div>
</div>
<div
className="settingsSection"
>
<div
className="settingsSectionPart settingsSectionInlineCheckbox"
> >
<div <div
className="settingsSectionLabel" className="settingsSectionLabel"
@@ -371,7 +130,7 @@ exports[`Settings Pane should render Default properly 1`] = `
className="settingsSection" className="settingsSection"
> >
<div <div
className="settingsSectionPart settingsSectionInlineCheckbox" className="settingsSectionPart"
> >
<div <div
className="settingsSectionLabel" className="settingsSectionLabel"
@@ -457,139 +216,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
> >
<div <div
className="settingsSectionPart" className="settingsSectionPart"
>
<div
className="settingsSectionLabel"
>
Retry Settings
<InfoTooltip>
Retry policy associated with throttled requests during CosmosDB queries.
</InfoTooltip>
</div>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryRetryAttemptsLabel"
>
Max retry attempts
</legend>
<InfoTooltip>
Max number of retries to be performed for a request. Default value 9.
</InfoTooltip>
</div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1"
incrementButtonAriaLabel="Increase value by 1"
labelPosition={0}
min={1}
onChange={[Function]}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={1}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
value="9"
/>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryRetryAttemptsLabel"
>
Fixed retry interval (ms)
</legend>
<InfoTooltip>
Fixed retry interval in milliseconds to wait between each retry ignoring the retryAfter returned as part of the response. Default value is 0 milliseconds.
</InfoTooltip>
</div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1000"
incrementButtonAriaLabel="Increase value by 1000"
labelPosition={0}
min={1000}
onChange={[Function]}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={1000}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
value="0"
/>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="queryRetryAttemptsLabel"
>
Max wait time (s)
</legend>
<InfoTooltip>
Max wait time in seconds to wait for a request while the retries are happening. Default value 30 seconds.
</InfoTooltip>
</div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1"
incrementButtonAriaLabel="Increase value by 1"
labelPosition={0}
min={1}
onChange={[Function]}
onDecrement={[Function]}
onIncrement={[Function]}
onValidate={[Function]}
step={1}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
value="30"
/>
</div>
</div>
<div
className="settingsSection"
>
<div
className="settingsSectionPart settingsSectionInlineCheckbox"
> >
<div <div
className="settingsSectionLabel" className="settingsSectionLabel"

View File

@@ -357,7 +357,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
value="" value=""
> >
<div <div
className="ms-TextField is-required root-110" className="ms-TextField is-required root-54"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
@@ -648,7 +648,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
} }
> >
<label <label
className="ms-Label root-121" className="ms-Label root-65"
htmlFor="TextField0" htmlFor="TextField0"
id="TextFieldLabel2" id="TextFieldLabel2"
> >
@@ -657,13 +657,13 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
</LabelBase> </LabelBase>
</StyledLabelBase> </StyledLabelBase>
<div <div
className="ms-TextField-fieldGroup fieldGroup-111" className="ms-TextField-fieldGroup fieldGroup-55"
> >
<input <input
aria-invalid={false} aria-invalid={false}
aria-labelledby="TextFieldLabel2" aria-labelledby="TextFieldLabel2"
autoFocus={true} autoFocus={true}
className="ms-TextField-field field-112" className="ms-TextField-field field-56"
id="TextField0" id="TextField0"
name="collectionIdConfirmation" name="collectionIdConfirmation"
onBlur={[Function]} onBlur={[Function]}
@@ -1569,7 +1569,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1595,7 +1595,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -1607,16 +1607,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1647,7 +1645,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"display": "inline-block", "display": "inline-block",
"padding": "0 16px", "padding": "0 16px",
"selectors": Object { "selectors": Object {
":active > span": Object { ":active > *": Object {
"left": 0, "left": 0,
"position": "relative", "position": "relative",
"top": 0, "top": 0,
@@ -1674,7 +1672,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -1699,16 +1697,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid transparent", "border": "1px solid transparent",
"borderRadius": undefined,
"bottom": 2, "bottom": 2,
"content": "\\"\\"", "content": "\\"\\"",
"left": 2, "left": 2,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1742,7 +1738,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"backgroundColor": "#f3f2f1", "backgroundColor": "#f3f2f1",
"color": "#d2d0ce", "color": "#d2d0ce",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1759,7 +1755,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"border": "1px solid #106ebe", "border": "1px solid #106ebe",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"borderColor": "Highlight", "borderColor": "Highlight",
"color": "Window", "color": "Window",
@@ -1771,7 +1767,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"border": "1px solid #005a9e", "border": "1px solid #005a9e",
"color": "#ffffff", "color": "#ffffff",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"borderColor": "WindowText", "borderColor": "WindowText",
@@ -1787,13 +1783,12 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"overflow": "hidden", "overflow": "hidden",
"padding": 0, "padding": 0,
"position": "absolute", "position": "absolute",
"whiteSpace": "nowrap",
"width": 1, "width": 1,
}, },
"splitButtonContainer": Array [ "splitButtonContainer": Array [
Object { Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
}, },
}, },
@@ -1804,16 +1799,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": "none",
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -1836,21 +1829,13 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderRight": "none", "borderRight": "none",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
}, },
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"border": "none", "border": "none",
"borderBottomRightRadius": "0", "borderBottomRightRadius": "0",
"borderTopRightRadius": "0", "borderTopRightRadius": "0",
"flexGrow": "1",
"selectors": Object { "selectors": Object {
":active": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none",
},
":hover": Object {
"border": "none",
},
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid WindowText", "border": "1px solid WindowText",
@@ -1863,7 +1848,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "1px solid WindowText", "border": "1px solid WindowText",
"borderLeftWidth": "0", "borderLeftWidth": "0",
}, },
@@ -1876,7 +1861,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -1890,7 +1875,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
"color": "Window", "color": "Window",
@@ -1904,7 +1889,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"border": "none", "border": "none",
"outline": "none", "outline": "none",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
@@ -1920,7 +1905,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Highlight", "backgroundColor": "Highlight",
"color": "Window", "color": "Window",
}, },
@@ -1929,7 +1914,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
".ms-Button.is-disabled": Object { ".ms-Button.is-disabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -1945,7 +1930,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
}, },
}, },
@@ -1957,7 +1942,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "WindowText", "backgroundColor": "WindowText",
}, },
}, },
@@ -1970,7 +1955,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"position": "absolute", "position": "absolute",
"right": 31, "right": 31,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "GrayText", "backgroundColor": "GrayText",
}, },
}, },
@@ -1992,18 +1977,18 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
":hover": Object { ":hover": Object {
"backgroundColor": "#106ebe", "backgroundColor": "#106ebe",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "Highlight", "color": "Highlight",
}, },
}, },
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Canvas", "backgroundColor": "WindowText",
}, },
}, },
}, },
Object { Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"color": "WindowText", "color": "WindowText",
}, },
@@ -2053,7 +2038,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Button--primary": Object { ".ms-Button--primary": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
@@ -2062,7 +2047,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
}, },
".ms-Button-menuIcon": Object { ".ms-Button-menuIcon": Object {
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -2070,7 +2055,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"backgroundColor": "Window", "backgroundColor": "Window",
"border": "1px solid GrayText", "border": "1px solid GrayText",
"color": "GrayText", "color": "GrayText",
@@ -2092,16 +2077,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"selectors": Object { "selectors": Object {
".ms-Fabric--isFocusVisible &:focus:after": Object { ".ms-Fabric--isFocusVisible &:focus:after": Object {
"border": "1px solid #ffffff", "border": "1px solid #ffffff",
"borderRadius": undefined,
"bottom": 3, "bottom": 3,
"content": "\\"\\"", "content": "\\"\\"",
"left": 3, "left": 3,
"outline": "1px solid #605e5c", "outline": "1px solid #605e5c",
"pointerEvents": undefined,
"position": "absolute", "position": "absolute",
"right": 3, "right": 3,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"border": "none", "border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
@@ -2123,7 +2106,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"splitButtonMenuIconDisabled": Object { "splitButtonMenuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)": Object { "@media screen and (-ms-high-contrast: active), (forced-colors: active)": Object {
"color": "GrayText", "color": "GrayText",
}, },
}, },
@@ -2413,7 +2396,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
> >
<button <button
aria-label="Create" aria-label="Create"
className="ms-Button ms-Button--primary root-122" className="ms-Button ms-Button--primary root-66"
data-is-focusable={true} data-is-focusable={true}
id="sidePanelOkButton" id="sidePanelOkButton"
onClick={[Function]} onClick={[Function]}
@@ -2425,16 +2408,16 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
type="submit" type="submit"
> >
<span <span
className="ms-Button-flexContainer flexContainer-123" className="ms-Button-flexContainer flexContainer-67"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<span <span
className="ms-Button-textContainer textContainer-124" className="ms-Button-textContainer textContainer-68"
> >
<span <span
className="ms-Button-label label-126" className="ms-Button-label label-70"
id="id__5" id="id__3"
key="id__5" key="id__3"
> >
Create Create
</span> </span>

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