You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by di...@apache.org on 2024/02/21 10:13:22 UTC

(superset) branch diego/ch78628/fix-disabled-ssh-toggle updated (1ec5cf8b6e -> 311a318340)

This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a change to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git


    from 1ec5cf8b6e Merge SSHTunnelSwitch components
     new 8e9b13dd04 Remove tunnel via database update
     new 0f73196d7e Lint
     add 91ac575eab chore(translations): updating pot -> po -> json files (babel 2.9.1) (#26773)
     add 827864b939 chore: Migrate AlteredSliceTag to typescript (#27030)
     add 847ed3f5b0 refactor: Ensure Flask framework leverages the Flask-SQLAlchemy session (Phase II) (#26909)
     add e4a723d840 fix: try to fix cypress with magic (#27098)
     add a912fafb6d chore(plugins): Update dropdown control for BigNumber with Time Comparison range (#27090)
     add 13f1642c73 feat(plugins): Tooltips on BigNumber with Time Comparison chart (#27092)
     add af577d64b1 fix: Timeseries Y-axis format with contribution mode (#27106)
     add 152cd70b5f fix: upgrade cryptography to major 42 (#27113)
     add 7b59c94827 fix(sqllab): flaky json explore modal due to over-rendering (#26791)
     add b60938be4d chore: bump firebolt-sqlalchemy to support service account auth (#27118)
     add 9514300da5 fix(docker): *-dev tags target right stage from Dockerfile (#27116)
     add 3224ac1818 chore(internet_port): added new ports and removed unnecessary string class (#27078)
     add e43097329f fix: bump grpcio, urllib3 and paramiko (#27124)
     add 753ef69529 feat(storybook): Co-habitating/Upgrading Storybooks to v7 (dependency madness ensues) (#26907)
     add c96e38c07c fix: removes old deprecated sqllab endpoints (#27117)
     add 8235d59e56 fix: gevent upgrade to 23.9.1 (#27112)
     add 3d645fd8e9 refactor: Updates some database columns to MediumText (#27119)
     add 506ea756ad fix: RLS modal overflow (#27128)
     add 60fe58196a fix(pivot-table-v2): Added forgotten translation pivot table v2 (#22840)
     add 04be96b0dc docs(miscellaneous): Export Datasoruces: export datasources exports to ZIP (#27120)
     add d2910b0b87 chore: lower cryptography min version to 41.0.2 (#27129)
     add eabee9dedd chore(hail mary): Update package-lock.json via npm-audit-fix (#26693)
     add 7c7deb960d fix: ID param for DELETE ssh_tunnel endpoint (#27130)
     add acb2d1d6b4 fix: Plain error message when visiting a dashboard via permalink without permissions (#27132)
     add 1ff4f79d5b docs: add Geotab to users list (#27134)
     add cf33a6213d fix: Duplicated toast messages (#27135)
     new 977f8f16b4 Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle
     new a17aac7119 Validate inputs
     add dab44c0b6f fix(plugins): Apply dashboard filters to comparison query in BigNumber with Time Comparison chart (#27138)
     add 8749d9f386 chore(tests): Remove unnecessary explicit Flask-SQLAlchemy session expunges (#27136)
     add 1776405903 refactor: Migrate ErrorBoundary to typescript (#27143)
     new 87fc78e12a Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle
     new b94994f07f Catch missing database port for SSH Tunnel
     new 7bc2283027 Raise SSHTunnelDatabasePortError
     new beba29db72 Clean up
     new 5f37e1f54b Add SSHTunnelSwitch test
     add 601e62a2ee feat(Alerts and Reports): Modal redesign (#26202)
     add 8dc6cbe206 fix(ci): mypy pre-commit issues (#27161)
     add f7c5773a97 feat: bump FAB to 4.4.0 (#27159)
     add 3eedcb4a2f fix: unlock and bump werkzeug (#27164)
     add c4982d4f29 docs: add Dropit Shopping to users list (#27166)
     add e0f4f34f97 chore: Updates CHANGELOG.md with 3.0.4 data (#27169)
     add b8fc2e57cc feat(adt): add 403 to api response status codes (#27162)
     add ceda51617b fix: CSRF exempt unit_tests (#27168)
     add c50dd3690a chore: Updates CHANGELOG.md with 3.1.1 data (#27170)
     add 986e4178f5 chore: Remove obsolete actor (#27147)
     add 3818da8509 feat(helm): optionally set pod disruption budgets (#27163)
     new 311a318340 Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

The 10 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .github/ISSUE_TEMPLATE/bug-report.yml              |     4 +-
 .github/workflows/dependency-review.yml            |     2 +
 .github/workflows/superset-python-unittest.yml     |     4 +
 .gitignore                                         |     1 +
 .pre-commit-config.yaml                            |     2 +-
 .pylintrc                                          |     2 +-
 CHANGELOG.md                                       |     2 +
 CHANGELOG/3.0.4.md                                 |    72 +
 CHANGELOG/3.1.1.md                                 |    75 +
 RESOURCES/INTHEWILD.md                             |     2 +
 UPDATING.md                                        |     4 +
 docs/docs/databases/firebolt.mdx                   |    11 +-
 .../docs/databases/installing-database-drivers.mdx |     2 +-
 .../importing-exporting-datasources.mdx            |     2 +-
 helm/superset/Chart.yaml                           |     2 +-
 helm/superset/README.md                            |    22 +-
 helm/superset/templates/_helpers.tpl               |    25 +
 helm/superset/templates/deployment-beat.yaml       |     3 +-
 helm/superset/templates/deployment-flower.yaml     |     3 +-
 helm/superset/templates/deployment-worker.yaml     |     3 +-
 helm/superset/templates/deployment-ws.yaml         |     3 +-
 helm/superset/templates/deployment.yaml            |     3 +-
 helm/superset/templates/pdb-beat.yaml              |    45 +
 helm/superset/templates/pdb-flower.yaml            |    45 +
 helm/superset/templates/pdb-worker.yaml            |    45 +
 helm/superset/templates/pdb-ws.yaml                |    45 +
 helm/superset/templates/pdb.yaml                   |    45 +
 helm/superset/values.yaml                          |    41 +-
 requirements/base.txt                              |    31 +-
 requirements/development.txt                       |     2 -
 requirements/docker.txt                            |     2 +-
 requirements/integration.txt                       |     6 -
 requirements/testing.txt                           |     8 +-
 scripts/build_docker.py                            |     1 +
 setup.py                                           |     9 +-
 superset-frontend/.eslintrc.js                     |    63 +-
 superset-frontend/.storybook/main.js               |    33 +-
 superset-frontend/.storybook/preview.jsx           |    12 +-
 superset-frontend/cypress-base/cypress.config.ts   |     4 +
 .../cypress-base/cypress/e2e/sqllab/query.test.ts  |     3 -
 superset-frontend/package-lock.json                | 48993 ++++++++++---------
 superset-frontend/package.json                     |    62 +-
 .../src/components/Select.tsx                      |     4 +-
 .../superset-ui-chart-controls/src/index.ts        |     1 +
 .../src/operators/contributionOperator.ts          |    25 +-
 .../src/operators/timeCompareOperator.ts           |    41 +-
 .../src/operators/timeComparePivotOperator.ts      |    51 +-
 .../superset-ui-chart-controls/src/types.ts        |    70 +-
 .../src/chart/components/ChartDataProvider.tsx     |     2 +-
 .../src/chart/components/SuperChart.tsx            |     1 -
 .../src/chart/components/createLoadableRenderer.ts |     4 +-
 .../src/chart/models/ChartPlugin.ts                |     2 +-
 .../src/connection/callApi/parseResponse.ts        |     8 +-
 .../src/number-format/NumberFormats.ts             |     6 +-
 .../superset-ui-core/src/query/api/v1/makeApi.ts   |     9 +-
 .../superset-ui-core/src/query/api/v1/types.ts     |     4 +-
 .../superset-ui-core/src/query/types/Operator.ts   |     6 +-
 .../test/chart/components/MockChartPlugins.tsx     |     4 +-
 .../packages/superset-ui-demo/.babelrc.json        |    18 +
 .../packages/superset-ui-demo/.storybook/main.js   |    30 +-
 .../superset-ui-demo/.storybook/preview.js         |    20 +-
 .../superset-ui-demo/.storybook/storybook.css      |     6 +-
 .../packages/superset-ui-demo/package.json         |    21 +-
 .../shared/components/ResizableChartDemo.tsx       |     4 +-
 .../storybook/shared/components/ResizablePanel.tsx |     6 +-
 ...{createQueryStory.tsx => createQuery.story.tsx} |    39 +-
 ...s.tsx => LegacyPluginChartCalendar.stories.tsx} |     0
 ...ries.tsx => LegacyPluginChartChord.stories.tsx} |     0
 ...ountryMapStories.tsx => CountryMap.stories.tsx} |    49 +-
 .../{Stories.jsx => EventFlow.stories.jsx}         |     0
 .../{Stories.tsx => Heatmap.stories.tsx}           |     0
 .../{Stories.tsx => Histogram.stories.tsx}         |     0
 .../{Stories.tsx => Horizon.stories.tsx}           |     0
 .../{Stories.tsx => MapBox.stories.tsx}            |     0
 .../{Stories.tsx => TTest.stories.tsx}             |     0
 ...Stories.tsx => ParallelCoordinates.stories.tsx} |     0
 .../{Stories.tsx => Partition.stories.tsx}         |     0
 .../{Stories.tsx => Rose.stories.tsx}              |     0
 .../{Stories.tsx => SankeyLoop.stories.tsx}        |     0
 .../{Stories.tsx => Sankey.stories.tsx}            |     0
 .../{Stories.tsx => WorldMap.stories.tsx}          |     0
 ...{BigNumberStories.tsx => BigNumber.stories.tsx} |     0
 ...TotalStories.tsx => BigNumberTotal.stories.tsx} |     0
 .../Arc/{Stories.tsx => Arc.stories.tsx}           |     0
 .../Grid/{Stories.tsx => Grid.stories.tsx}         |     0
 .../Hex/{Stories.tsx => Hex.stories.tsx}           |     0
 .../Path/{Stories.tsx => Path.stories.tsx}         |     0
 .../Polygon/{Stories.tsx => Polygon.stories.tsx}   |     0
 .../Scatter/{Stories.tsx => Scatter.stories.tsx}   |     0
 .../Area/{Stories.tsx => NVD3Area.stories.tsx}     |     0
 .../Area/stories/stackedWithBounds.tsx             |     8 +-
 .../Bar/{Stories.tsx => NVD3Bar.stories.tsx}       |     0
 .../{Stories.tsx => NVD3BoxPlot.stories.tsx}       |     0
 .../Bubble/{Stories.tsx => NVD3Bubble.stories.tsx} |     0
 .../Bullet/{Stories.tsx => NVD3Bullet.stories.tsx} |     0
 .../{Stories.tsx => NVD3Compare.stories.tsx}       |     0
 .../{Stories.tsx => NVD3DistBar.stories.tsx}       |     0
 .../Line/{Stories.tsx => NVD3Line.stories.tsx}     |     0
 .../Pie/{Stories.tsx => NVD3Pie.stories.tsx}       |     0
 .../AreaSeries/AreaSeries.stories.tsx              |   198 +
 .../{AreaTimeseries => AreaSeries}/data.ts         |     0
 .../AreaTimeseries/Stories.tsx                     |    88 -
 .../BoxPlot/{Stories.tsx => BoxPolot.stories.tsx}  |    28 +-
 .../{Stories.tsx => BubbleChart.stories.tsx}       |    95 +-
 .../Funnel/{Stories.tsx => Funnel.stories.tsx}     |    79 +-
 .../Gauge/{Stories.tsx => Gauge.stories.tsx}       |     3 +-
 .../Graph/{Stories.tsx => Graph.stories.tsx}       |     3 +-
 .../MixedSeries/MixedSeries.stories.tsx            |   352 +
 .../negativeData.ts                                |     0
 .../MixedTimeseries/Stories.tsx                    |   173 -
 .../plugin-chart-echarts/Pie/Pie.stories.tsx       |   195 +
 .../plugins/plugin-chart-echarts/Pie/Stories.tsx   |   108 -
 .../Radar/{Stories.tsx => Radar.stories.tsx}       |     3 +-
 .../Sunburst/{Stories.tsx => Sunburst.stories.tsx} |    26 +-
 .../{Stories.tsx => Timeseries.stories.tsx}        |   149 +-
 .../Tree/{Stories.tsx => Tree.stories.tsx}         |   126 +-
 .../Treemap/{Stories.tsx => Treemap.stories.tsx}   |    41 +-
 .../{Stories.tsx => Waterfall.stories.tsx}         |     5 +-
 ...ivotTableStories.tsx => PivotTable.stories.tsx} |    25 +-
 .../{TableStories.tsx => Table.stories.tsx}        |   101 +-
 .../{Stories.tsx => WordCloud.stories.tsx}         |    56 +-
 ...erStories.tsx => ChartDataProvider.stories.tsx} |   128 +-
 .../superset-ui-chart/SuperChart.stories.tsx       |   255 +
 .../superset-ui-chart/SuperChartStories.tsx        |   169 -
 ...lettesStories.tsx => ColorPalettes.stories.tsx} |     0
 ...onnectionStories.tsx => Connection.stories.tsx} |    69 +-
 ...gNumberStories.tsx => NumberFormat.stories.tsx} |     0
 .../{ThemeStories.tsx => Theme.stories.tsx}        |     0
 ...imeFormatStories.tsx => TimeFormat.stories.tsx} |     0
 .../superset-ui-switchboard/src/switchboard.ts     |     2 +-
 .../legacy-plugin-chart-heatmap/src/Heatmap.js     |     2 +-
 .../src/MixedTimeseries/transformProps.ts          |    23 +-
 .../src/Timeseries/constants.ts                    |    25 +-
 .../src/Timeseries/transformProps.ts               |     8 +-
 .../src/Waterfall/transformProps.ts                |     6 +-
 .../plugin-chart-echarts/src/utils/forecast.ts     |    15 +-
 .../plugin-chart-echarts/src/utils/formatters.ts   |    11 +-
 .../test/utils/formatters.test.ts}                 |    37 +-
 .../src/PopKPI.tsx                                 |    46 +-
 .../src/plugin/buildQuery.ts                       |    10 +-
 .../src/plugin/controlPanel.ts                     |    12 +-
 .../src/plugin/transformProps.ts                   |    14 +
 .../src/types.ts                                   |     1 +
 .../src/utils.ts                                   |    45 +-
 .../src/react-pivottable/TableRenderers.jsx        |     6 +-
 .../src/react-pivottable/utilities.js              |     3 +-
 .../src/DataTable/hooks/useSticky.tsx              |     3 +-
 .../plugins/plugin-chart-table/src/TableChart.tsx  |     4 +-
 .../plugin-chart-table/src/transformProps.ts       |     3 +-
 .../plugin-chart-table/src/utils/formatValue.ts    |    10 +-
 .../src/chart/WordCloud.tsx                        |    11 +-
 superset-frontend/src/GlobalStyles.tsx             |     1 +
 .../SqlLab/components/AceEditorWrapper/index.tsx   |     4 +-
 .../components/AceEditorWrapper/useAnnotations.ts  |    18 +-
 .../src/SqlLab/components/App/App.test.tsx         |    20 +-
 .../src/SqlLab/components/App/index.tsx            |     3 +
 .../components/QueryHistory/QueryHistory.test.tsx  |     5 +-
 .../src/SqlLab/components/QueryHistory/index.tsx   |    29 +-
 .../QueryLimitSelect/QueryLimitSelect.test.tsx     |    12 +-
 .../src/SqlLab/components/QueryTable/index.tsx     |     3 +-
 .../SqlLab/components/ResultSet/ResultSet.test.tsx |   333 +-
 .../src/SqlLab/components/ResultSet/index.tsx      |    52 +-
 .../{SouthPane.test.tsx => Results.test.tsx}       |    87 +-
 .../src/SqlLab/components/SouthPane/Results.tsx    |   106 +
 .../SqlLab/components/SouthPane/SouthPane.test.tsx |    81 +-
 .../src/SqlLab/components/SouthPane/index.tsx      |   174 +-
 .../SqlLab/components/SqlEditor/SqlEditor.test.tsx |     7 +-
 .../src/SqlLab/components/SqlEditor/index.tsx      |    72 +-
 .../SqlLab/components/TabbedSqlEditors/index.tsx   |     1 -
 .../components/TableElement/TableElement.test.tsx  |     5 +-
 .../src/SqlLab/components/TableElement/index.tsx   |     4 +-
 .../src/SqlLab/reducers/getInitialState.ts         |     4 -
 .../src/SqlLab/utils/newQueryTabName.ts            |     4 +-
 superset-frontend/src/assets/staticPages/404.html  |     2 +-
 superset-frontend/src/assets/staticPages/500.html  |     2 +-
 .../src/assets/stylesheets/less/variables.less     |     8 +-
 .../src/components/Alert/Alert.stories.tsx         |    25 +-
 .../AlteredSliceTag/AlteredSliceTag.stories.tsx    |     8 -
 ...redSliceTagMocks.js => AlteredSliceTagMocks.ts} |    11 +-
 .../AlteredSliceTag/{index.jsx => index.tsx}       |   133 +-
 .../AsyncAceEditor/AsyncAceEditor.stories.tsx      |    11 +-
 .../AsyncEsmComponent.stories.tsx                  |     8 -
 .../src/components/Badge/Badge.stories.tsx         |    42 +-
 .../src/components/Button/Button.stories.tsx       |    25 +-
 superset-frontend/src/components/Button/index.tsx  |    11 +-
 .../components/ButtonGroup/ButtonGroup.stories.tsx |    11 +-
 .../src/components/Card/Card.stories.tsx           |     8 -
 .../CertifiedBadge/CertifiedBadge.stories.tsx      |     8 -
 .../src/components/Chart/ChartRenderer.jsx         |     2 +-
 .../src/components/Chart/DrillBy/DrillByModal.tsx  |     4 +-
 .../Chart/DrillBy/useDisplayModeToggle.tsx         |     4 +-
 .../DrillDetail/DrillDetailMenuItems.test.tsx      |     7 +-
 .../Chart/DrillDetail/DrillDetailPane.tsx          |    15 +-
 .../src/components/Collapse/Collapse.stories.tsx   |    11 +-
 .../ConfirmStatusChange.stories.tsx                |     8 -
 .../CopyToClipboard/CopyToClipboard.stories.tsx    |     8 -
 .../components/CronPicker/CronPicker.stories.tsx   |     8 -
 .../src/components/CronPicker/CronPicker.tsx       |   115 +-
 .../components/DatePicker/DatePicker.stories.tsx   |    16 -
 .../src/components/DesignSystem.stories.mdx        |    25 -
 .../src/components/DesignSystem.stories.tsx        |    54 +
 .../src/components/Dropdown/Dropdown.stories.tsx   |     8 -
 .../DropdownButton/DropdownButton.stories.tsx      |     8 -
 .../DropdownContainer/Overview.stories.mdx         |    17 -
 .../Overview.stories.tsx}                          |    49 +-
 .../DropdownSelectableIcon.stories.tsx             |     8 -
 .../components/DropdownSelectableIcon/index.tsx    |    33 +-
 .../EditableTitle/EditableTitle.stories.tsx        |     8 -
 .../ErrorBoundary/ErrorBoundary.test.tsx           |     8 +-
 .../ErrorBoundary/{index.jsx => index.tsx}         |    49 +-
 .../ErrorMessage/BasicErrorAlert.test.tsx          |     5 +-
 .../ErrorMessage/DatabaseErrorMessage.test.tsx     |     5 +-
 .../DatasetNotFoundErrorMessage.test.tsx           |     5 +-
 .../components/ErrorMessage/ErrorAlert.test.tsx    |     5 +-
 .../ErrorMessageWithStackTrace.test.tsx            |     5 +-
 .../ErrorMessage/ParameterErrorMessage.test.tsx    |     5 +-
 .../ErrorMessage/TimeoutErrorMessage.test.tsx      |     5 +-
 .../src/components/FacePile/FacePile.stories.tsx   |     9 +-
 .../FilterableTable/FilterableTable.stories.tsx    |     8 -
 .../src/components/FormRow/FormRow.stories.tsx     |     8 -
 .../components/IconTooltip/IconTooltip.stories.tsx |     8 -
 .../src/components/Icons/Icons.stories.tsx         |     8 -
 .../IndeterminateCheckbox.stories.tsx              |     8 -
 .../components/InfoTooltip/InfoTooltip.stories.tsx |     8 -
 .../ListViewCard/ListViewCard.stories.tsx          |    41 +-
 .../src/components/Loading/Loading.stories.tsx     |    25 +-
 .../components/MessageToasts/ToastPresenter.tsx    |     3 +-
 .../getToastsFromPyFlashMessages.test.js           |    48 -
 .../components/MetadataBar/MetadataBar.stories.mdx |   145 -
 .../components/MetadataBar/MetadataBar.stories.tsx |     8 -
 .../MetadataBar/MetadataBarOverview.stories.tsx    |   177 +
 .../src/components/Modal/Modal.stories.tsx         |     8 -
 superset-frontend/src/components/Modal/Modal.tsx   |     8 +-
 .../PopoverDropdown/PopoverDropdown.stories.tsx    |     8 -
 .../PopoverSection/PopoverSection.stories.tsx      |     8 -
 .../RefreshLabel/RefreshLabel.stories.tsx          |     8 -
 .../src/components/Select/AsyncSelect.stories.tsx  |     8 -
 .../src/components/Select/AsyncSelect.test.tsx     |     4 +-
 .../src/components/Select/Select.stories.tsx       |    42 +-
 .../src/components/Slider/Slider.stories.tsx       |     8 -
 .../src/components/Switch/Switch.stories.tsx       |     8 -
 ...able.overview.mdx => TableOverview.stories.tsx} |   152 +-
 .../ActionCell/ActionCell.overview.mdx             |    69 -
 .../ActionCell/ActionCell.overview.tsx}            |    37 +-
 .../TableSelector/TableSelector.test.tsx           |     2 +-
 .../src/components/TableSelector/index.tsx         |     5 +-
 .../src/components/TableView/TableView.stories.tsx |     8 -
 .../src/components/Tabs/Tabs.stories.tsx           |     8 -
 .../src/components/Tags/TagsList.stories.tsx       |     8 -
 .../src/components/Timer/Timer.stories.tsx         |    11 +-
 .../src/components/TimezoneSelector/index.tsx      |     4 +-
 .../src/components/Tooltip/Tooltip.stories.tsx     |     8 -
 .../TooltipParagraph/TooltipParagraph.stories.tsx  |     8 -
 .../src/components/UiConfigContext/index.tsx       |    31 +-
 .../WarningIconWithTooltip.stories.tsx             |     8 -
 .../components/AnchorLink/AnchorLink.stories.tsx   |     8 -
 .../src/dashboard/components/Dashboard.jsx         |     2 -
 .../src/dashboard/components/Dashboard.test.jsx    |     1 -
 .../DashboardBuilder/DashboardBuilder.tsx          |    85 +-
 .../dashboard/components/DashboardGrid.test.jsx    |    21 +-
 .../components/FiltersBadge/FiltersBadge.test.tsx  |     5 +-
 .../SliceHeaderControls.test.tsx                   |     2 +-
 .../components/filterscope/FilterScope.test.tsx    |     4 +-
 .../components/gridComponents/Column.test.jsx      |    33 +-
 .../components/gridComponents/Row.test.jsx         |    33 +-
 .../components/gridComponents/Tabs.test.jsx        |    19 +-
 .../gridComponents/new/NewColumn.test.jsx          |     5 +-
 .../gridComponents/new/NewDivider.test.jsx         |     5 +-
 .../gridComponents/new/NewHeader.test.jsx          |     5 +-
 .../components/gridComponents/new/NewRow.test.jsx  |     5 +-
 .../components/gridComponents/new/NewTabs.test.jsx |     5 +-
 .../components/menu/BackgroundStyleDropdown.tsx    |     7 +-
 .../FilterBarSettings/FilterBarSettings.test.tsx   |     1 -
 .../FilterBar/FilterControls/FilterControls.tsx    |     4 +-
 .../FilterControls/FilterDivider.stories.tsx       |     8 -
 .../nativeFilters/FilterBar/Header/index.tsx       |     4 +-
 .../nativeFilters/FilterBar/Horizontal.tsx         |     4 +-
 .../nativeFilters/FilterCard/useFilterScope.ts     |     9 +-
 .../FiltersConfigForm/ColumnSelect.tsx             |     4 +-
 .../FiltersConfigForm/FilterScope/FilterScope.tsx  |     5 +-
 .../src/dashboard/containers/Dashboard.ts          |     1 -
 superset-frontend/src/dashboard/reducers/types.ts  |     2 +-
 superset-frontend/src/dashboard/types.ts           |     3 +-
 .../explore/components/ControlHeader.stories.tsx   |     8 -
 .../explore/components/ControlPanelsContainer.tsx  |     4 +-
 .../explore/components/DataTableControl/index.tsx  |     4 +-
 .../components/DataTablesPane/test/fixture.tsx     |     6 +-
 .../ExploreChartHeader/ExploreChartHeader.test.tsx |     5 +-
 .../PropertiesModal/PropertiesModal.test.tsx       |     2 +-
 .../RunQueryButton/RunQueryButton.stories.tsx      |     8 -
 .../components/controls/BoundsControl.stories.tsx  |     8 -
 .../ColorSchemeControl/ColorSchemeControl.test.tsx |     2 +-
 .../ColumnConfigControl/ColumnConfigPopover.tsx    |     4 +-
 .../ColumnConfigControl/ControlForm/index.tsx      |     2 +-
 .../controls/ColumnConfigControl/types.ts          |     2 +-
 .../DatasourceControl/DatasourceControl.test.tsx   |     4 +-
 .../DndColumnSelectControl/ColumnSelectPopover.tsx |     4 +-
 .../FixedOrMetricControl.test.tsx                  |     5 +-
 .../controls/MetricControl/AdhocMetric.js          |     4 +-
 .../MetricControl/AdhocMetricEditPopoverTitle.tsx  |   135 +-
 .../controls/VizTypeControl/FastVizSwitcher.tsx    |     3 +-
 .../src/features/alerts/AlertReportModal.test.jsx  |   367 -
 .../src/features/alerts/AlertReportModal.test.tsx  |   632 +-
 .../src/features/alerts/AlertReportModal.tsx       |  1296 +-
 .../alerts/buildErrorTooltipMessage.test.tsx       |    70 +
 .../alerts/buildErrorTooltipMessage.tsx}           |    53 +-
 .../components/AlertReportCronScheduler.test.tsx   |   153 -
 .../alerts/components/AlertReportCronScheduler.tsx |   161 +-
 .../alerts/components/NotificationMethod.tsx       |    31 +-
 .../alerts/components/NumberInput.tsx}             |    64 +-
 .../src/features/alerts/components/StyledPanel.tsx |    75 +
 .../alerts/components/ValidatedPanelHeader.tsx}    |    64 +-
 superset-frontend/src/features/alerts/types.ts     |    18 +
 .../databases/DatabaseModal/SSHTunnelForm.tsx      |    12 +-
 .../DatabaseModal/SSHTunnelSwitch.test.tsx         |   133 +
 .../databases/DatabaseModal/index.test.tsx         |    11 +-
 .../src/features/databases/DatabaseModal/index.tsx |    74 +-
 superset-frontend/src/features/databases/types.ts  |    32 +-
 .../AddDataset/DatasetPanel/DatasetPanel.test.tsx  |     5 +-
 .../DatasetMetadataBar.skipped-stories.tsx         |     8 -
 .../features/queries/SavedQueryPreviewModal.tsx    |   133 +-
 .../src/features/reports/ReportModal/index.tsx     |     9 +-
 .../src/features/rls/RowLevelSecurityModal.tsx     |    88 +-
 superset-frontend/src/features/rls/constants.ts    |     2 +-
 .../Select/SelectFilterPlugin.stories.tsx          |    14 +-
 superset-frontend/src/pages/Chart/Chart.test.tsx   |    11 +-
 .../src/pages/ExecutionLogList/index.tsx           |     4 +-
 superset-frontend/src/utils/urlUtils.ts            |     2 +-
 superset-frontend/src/views/CRUD/hooks.ts          |     2 +-
 .../src/visualizations/TimeTable/transformProps.ts |    13 +-
 superset-frontend/webpack.config.js                |    33 +-
 superset/advanced_data_type/api.py                 |     4 +
 .../advanced_data_type/plugins/internet_port.py    |    11 +-
 superset/cli/importexport.py                       |     3 +-
 superset/commands/chart/delete.py                  |     3 +-
 superset/commands/chart/importers/v1/__init__.py   |    10 +-
 superset/commands/chart/importers/v1/utils.py      |    13 +-
 superset/commands/dashboard/delete.py              |     3 +-
 .../commands/dashboard/importers/v1/__init__.py    |    19 +-
 superset/commands/dashboard/importers/v1/utils.py  |    11 +-
 superset/commands/database/create.py               |    10 +-
 superset/commands/database/delete.py               |     3 +-
 .../commands/database/importers/v1/__init__.py     |     8 +-
 superset/commands/database/importers/v1/utils.py   |    13 +-
 superset/commands/database/ssh_tunnel/create.py    |    11 +
 .../commands/database/ssh_tunnel/exceptions.py     |     4 +
 superset/commands/database/ssh_tunnel/update.py    |    15 +
 superset/commands/database/test_connection.py      |    52 +-
 superset/commands/database/update.py               |    77 +-
 superset/commands/database/validate_sql.py         |    10 +-
 superset/commands/dataset/importers/v0.py          |    57 +-
 superset/commands/dataset/importers/v1/__init__.py |     8 +-
 superset/commands/dataset/importers/v1/utils.py    |    20 +-
 superset/commands/explore/parameters.py            |     3 -
 superset/commands/importers/v1/__init__.py         |     6 +-
 superset/commands/importers/v1/assets.py           |    21 +-
 superset/commands/importers/v1/examples.py         |    11 +-
 superset/commands/query/importers/v1/__init__.py   |     8 +-
 superset/commands/query/importers/v1/utils.py      |    13 +-
 superset/commands/report/alert.py                  |     8 +-
 superset/config.py                                 |     2 +-
 superset/connectors/sqla/models.py                 |    17 +-
 superset/connectors/sqla/utils.py                  |     6 +-
 superset/daos/base.py                              |    13 +-
 superset/databases/api.py                          |    21 +-
 superset/databases/filters.py                      |     4 +-
 superset/db_engine_specs/gsheets.py                |     6 +-
 superset/explore/api.py                            |     3 +-
 superset/extensions/__init__.py                    |     2 +-
 superset/initialization/__init__.py                |     2 -
 ...14-43_17fcea065655_change_text_to_mediumtext.py |    87 +
 superset/models/annotations.py                     |     5 +-
 superset/models/core.py                            |     4 +-
 superset/models/dashboard.py                       |     4 +-
 superset/models/helpers.py                         |    18 +-
 superset/models/slice.py                           |     4 +-
 superset/models/sql_lab.py                         |    12 +-
 superset/reports/models.py                         |    13 +-
 superset/security/manager.py                       |    16 +-
 superset/sqllab/api.py                             |     4 +-
 superset/sqllab/execution_context_convertor.py     |     1 -
 superset/sqllab/schemas.py                         |     2 +-
 superset/tables/models.py                          |     2 +-
 superset/tags/models.py                            |    24 +-
 superset/translations/de/LC_MESSAGES/messages.json | 10142 ++--
 superset/translations/de/LC_MESSAGES/messages.po   | 33222 +++++++------
 superset/translations/en/LC_MESSAGES/messages.json |  7786 ++-
 superset/translations/en/LC_MESSAGES/messages.po   | 22882 +++++----
 superset/translations/es/LC_MESSAGES/messages.json |  6066 ++-
 superset/translations/es/LC_MESSAGES/messages.po   | 30539 ++++++------
 superset/translations/fr/LC_MESSAGES/messages.json |  7210 ++-
 superset/translations/fr/LC_MESSAGES/messages.po   | 32187 ++++++------
 superset/translations/it/LC_MESSAGES/messages.json |  6270 ++-
 superset/translations/it/LC_MESSAGES/messages.po   | 27830 +++++------
 superset/translations/ja/LC_MESSAGES/messages.json |  6299 ++-
 superset/translations/ja/LC_MESSAGES/messages.po   | 28673 +++++------
 superset/translations/ko/LC_MESSAGES/messages.json |  6363 ++-
 superset/translations/ko/LC_MESSAGES/messages.po   | 27202 +++++-----
 superset/translations/messages.pot                 | 22872 +++++----
 superset/translations/nl/LC_MESSAGES/messages.json |  7706 ++-
 superset/translations/nl/LC_MESSAGES/messages.po   | 28948 +++++------
 superset/translations/pt/LC_MESSAGES/messages.json |  6196 ++-
 superset/translations/pt/LC_MESSAGES/messages.po   | 28736 +++++------
 .../translations/pt_BR/LC_MESSAGES/messages.json   |  9781 ++--
 .../translations/pt_BR/LC_MESSAGES/messages.po     | 33114 +++++++------
 superset/translations/ru/LC_MESSAGES/messages.json |  9328 ++--
 superset/translations/ru/LC_MESSAGES/messages.po   | 32316 ++++++------
 superset/translations/sk/LC_MESSAGES/messages.json |  7714 ++-
 superset/translations/sk/LC_MESSAGES/messages.po   | 23418 +++++----
 superset/translations/sl/LC_MESSAGES/messages.json | 10412 ++--
 superset/translations/sl/LC_MESSAGES/messages.po   | 32495 ++++++------
 superset/translations/uk/LC_MESSAGES/messages.json | 10072 ++--
 superset/translations/uk/LC_MESSAGES/messages.po   | 24756 ++++++----
 superset/translations/zh/LC_MESSAGES/messages.json |  7451 ++-
 superset/translations/zh/LC_MESSAGES/messages.po   | 31489 ++++++------
 superset/utils/dashboard_import_export.py          |     7 +-
 superset/utils/dict_import_export.py               |     7 +-
 superset/views/api.py                              |     4 +-
 superset/views/core.py                             |    24 +-
 superset/views/sql_lab/views.py                    |    15 +-
 superset/viz.py                                    |     3 +-
 tests/integration_tests/async_events/api_tests.py  |     4 +-
 tests/integration_tests/base_tests.py              |    31 +-
 tests/integration_tests/cache_tests.py             |     4 +-
 tests/integration_tests/charts/api_tests.py        |    10 +-
 tests/integration_tests/charts/commands_tests.py   |     2 +-
 tests/integration_tests/charts/data/api_tests.py   |     2 +-
 tests/integration_tests/core_tests.py              |    39 +-
 tests/integration_tests/dashboards/api_tests.py    |     4 +-
 .../dashboards/filter_state/api_tests.py           |     7 +-
 .../dashboards/permalink/api_tests.py              |     6 +-
 .../dashboards/superset_factory_util.py            |    58 +-
 tests/integration_tests/databases/api_tests.py     |    10 +-
 tests/integration_tests/datasets/api_tests.py      |     1 -
 tests/integration_tests/datasource_tests.py        |    36 +-
 .../db_engine_specs/databricks_tests.py            |    16 +-
 .../db_engine_specs/hive_tests.py                  |    14 +-
 .../db_engine_specs/postgres_tests.py              |    24 +-
 .../db_engine_specs/presto_tests.py                |    14 +-
 .../integration_tests/dict_import_export_tests.py  |    26 +-
 tests/integration_tests/explore/api_tests.py       |    10 +-
 .../explore/form_data/api_tests.py                 |    10 +-
 .../explore/form_data/commands_tests.py            |    32 +-
 .../explore/permalink/api_tests.py                 |     3 +-
 .../explore/permalink/commands_tests.py            |    32 +-
 tests/integration_tests/fixtures/datasource.py     |    13 +-
 tests/integration_tests/import_export_tests.py     |    15 +-
 .../key_value/commands/fixtures.py                 |     3 +-
 .../security/guest_token_security_tests.py         |    15 +-
 .../security/migrate_roles_tests.py                |     5 +-
 .../security/row_level_security_tests.py           |    29 +-
 tests/integration_tests/security_tests.py          |    42 +-
 tests/integration_tests/sqllab_tests.py            |     6 +-
 tests/integration_tests/test_jinja_context.py      |    38 +-
 tests/integration_tests/utils/get_dashboards.py    |     5 +-
 tests/integration_tests/utils_tests.py             |     4 +-
 .../charts/commands/importers/v1/import_test.py    |    12 +-
 tests/unit_tests/charts/dao/dao_tests.py           |    16 +-
 tests/unit_tests/charts/test_post_processing.py    |     7 +-
 tests/unit_tests/columns/test_models.py            |     7 +-
 .../commands/importers/v1/assets_test.py           |    34 +-
 tests/unit_tests/config_test.py                    |     4 +-
 tests/unit_tests/conftest.py                       |     4 +-
 tests/unit_tests/dao/dataset_test.py               |     5 +-
 tests/unit_tests/dao/queries_test.py               |    80 +-
 tests/unit_tests/dao/tag_test.py                   |     2 +-
 .../commands/importers/v1/import_test.py           |    14 +-
 tests/unit_tests/dashboards/dao_tests.py           |    12 +-
 tests/unit_tests/databases/api_test.py             |    50 +-
 .../databases/commands/importers/v1/import_test.py |    27 +-
 tests/unit_tests/databases/dao/dao_tests.py        |    10 +-
 .../databases/ssh_tunnel/commands/create_test.py   |    12 +-
 .../databases/ssh_tunnel/commands/delete_test.py   |    10 +-
 .../databases/ssh_tunnel/commands/update_test.py   |    10 +-
 tests/unit_tests/databases/ssh_tunnel/dao_tests.py |     4 +-
 tests/unit_tests/datasets/api_tests.py             |     8 +-
 tests/unit_tests/datasets/commands/export_test.py  |     8 +-
 .../datasets/commands/importers/v1/import_test.py  |    58 +-
 tests/unit_tests/datasets/dao/dao_tests.py         |    10 +-
 tests/unit_tests/datasource/dao_tests.py           |    14 +-
 tests/unit_tests/db_engine_specs/test_druid.py     |    16 +-
 tests/unit_tests/db_engine_specs/test_pinot.py     |     8 +-
 tests/unit_tests/extensions/test_sqlalchemy.py     |    27 +-
 tests/unit_tests/queries/dao_test.py               |     4 +-
 tests/unit_tests/scripts/docker_build.py           |     2 +-
 tests/unit_tests/security/api_test.py              |     6 +-
 tests/unit_tests/sql_lab_test.py                   |    10 +-
 tests/unit_tests/sql_parse_tests.py                |     3 +-
 tests/unit_tests/tables/test_models.py             |    10 +-
 tests/unit_tests/tags/commands/create_test.py      |    29 +-
 tests/unit_tests/tags/commands/update_test.py      |    23 +-
 491 files changed, 326299 insertions(+), 314905 deletions(-)
 create mode 100644 CHANGELOG/3.0.4.md
 create mode 100644 CHANGELOG/3.1.1.md
 create mode 100644 helm/superset/templates/pdb-beat.yaml
 create mode 100644 helm/superset/templates/pdb-flower.yaml
 create mode 100644 helm/superset/templates/pdb-worker.yaml
 create mode 100644 helm/superset/templates/pdb-ws.yaml
 create mode 100644 helm/superset/templates/pdb.yaml
 create mode 100644 superset-frontend/packages/superset-ui-demo/.babelrc.json
 rename superset-frontend/packages/superset-ui-demo/storybook/shared/components/{createQueryStory.tsx => createQuery.story.tsx} (78%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-calendar/{Stories.tsx => LegacyPluginChartCalendar.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-chord/{Stories.tsx => LegacyPluginChartChord.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-country-map/{CountryMapStories.tsx => CountryMap.stories.tsx} (73%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-event-flow/{Stories.jsx => EventFlow.stories.jsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-heatmap/{Stories.tsx => Heatmap.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-histogram/{Stories.tsx => Histogram.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-horizon/{Stories.tsx => Horizon.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-map-box/{Stories.tsx => MapBox.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-paired-t-test/{Stories.tsx => TTest.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-parallel-coordinates/{Stories.tsx => ParallelCoordinates.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-partition/{Stories.tsx => Partition.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-rose/{Stories.tsx => Rose.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-sankey-loop/{Stories.tsx => SankeyLoop.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-sankey/{Stories.tsx => Sankey.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-plugin-chart-world-map/{Stories.tsx => WorldMap.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-big-number/BigNumber/{BigNumberStories.tsx => BigNumber.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-big-number/BigNumberTotal/{BigNumberTotalStories.tsx => BigNumberTotal.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-deckgl/Arc/{Stories.tsx => Arc.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-deckgl/Grid/{Stories.tsx => Grid.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-deckgl/Hex/{Stories.tsx => Hex.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-deckgl/Path/{Stories.tsx => Path.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-deckgl/Polygon/{Stories.tsx => Polygon.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-deckgl/Scatter/{Stories.tsx => Scatter.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/{Stories.tsx => NVD3Area.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Bar/{Stories.tsx => NVD3Bar.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/BoxPlot/{Stories.tsx => NVD3BoxPlot.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Bubble/{Stories.tsx => NVD3Bubble.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Bullet/{Stories.tsx => NVD3Bullet.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Compare/{Stories.tsx => NVD3Compare.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/DistBar/{Stories.tsx => NVD3DistBar.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Line/{Stories.tsx => NVD3Line.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Pie/{Stories.tsx => NVD3Pie.stories.tsx} (100%)
 create mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaSeries/AreaSeries.stories.tsx
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/{AreaTimeseries => AreaSeries}/data.ts (100%)
 delete mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/Stories.tsx
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/BoxPlot/{Stories.tsx => BoxPolot.stories.tsx} (79%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Bubble/{Stories.tsx => BubbleChart.stories.tsx} (64%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Funnel/{Stories.tsx => Funnel.stories.tsx} (57%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Gauge/{Stories.tsx => Gauge.stories.tsx} (94%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Graph/{Stories.tsx => Graph.stories.tsx} (94%)
 create mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/MixedSeries/MixedSeries.stories.tsx
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/{MixedTimeseries => MixedSeries}/negativeData.ts (100%)
 delete mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/MixedTimeseries/Stories.tsx
 create mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Pie/Pie.stories.tsx
 delete mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Pie/Stories.tsx
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Radar/{Stories.tsx => Radar.stories.tsx} (95%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/{Stories.tsx => Sunburst.stories.tsx} (79%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/{Stories.tsx => Timeseries.stories.tsx} (57%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Tree/{Stories.tsx => Tree.stories.tsx} (51%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Treemap/{Stories.tsx => Treemap.stories.tsx} (72%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Waterfall/{Stories.tsx => Waterfall.stories.tsx} (92%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/{PivotTableStories.tsx => PivotTable.stories.tsx} (83%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-table/{TableStories.tsx => Table.stories.tsx} (69%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-word-cloud/{Stories.tsx => WordCloud.stories.tsx} (81%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/{ChartDataProviderStories.tsx => ChartDataProvider.stories.tsx} (53%)
 create mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/SuperChart.stories.tsx
 delete mode 100644 superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/SuperChartStories.tsx
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-color/{ColorPallettesStories.tsx => ColorPalettes.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-connection/{ConnectionStories.tsx => Connection.stories.tsx} (59%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-number-format/{BigNumberStories.tsx => NumberFormat.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-style/{ThemeStories.tsx => Theme.stories.tsx} (100%)
 rename superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-time-format/{TimeFormatStories.tsx => TimeFormat.stories.tsx} (100%)
 rename superset-frontend/{src/components/MessageToasts/getToastsFromPyFlashMessages.js => plugins/plugin-chart-echarts/test/utils/formatters.test.ts} (55%)
 copy superset-frontend/src/SqlLab/components/SouthPane/{SouthPane.test.tsx => Results.test.tsx} (60%)
 create mode 100644 superset-frontend/src/SqlLab/components/SouthPane/Results.tsx
 rename superset-frontend/src/components/AlteredSliceTag/{AlteredSliceTagMocks.js => AlteredSliceTagMocks.ts} (91%)
 rename superset-frontend/src/components/AlteredSliceTag/{index.jsx => index.tsx} (68%)
 delete mode 100644 superset-frontend/src/components/DesignSystem.stories.mdx
 create mode 100644 superset-frontend/src/components/DesignSystem.stories.tsx
 delete mode 100644 superset-frontend/src/components/DropdownContainer/Overview.stories.mdx
 copy superset-frontend/src/components/{Tags/TagsList.stories.tsx => DropdownContainer/Overview.stories.tsx} (51%)
 rename superset-frontend/src/components/ErrorBoundary/{index.jsx => index.tsx} (64%)
 delete mode 100644 superset-frontend/src/components/MessageToasts/getToastsFromPyFlashMessages.test.js
 delete mode 100644 superset-frontend/src/components/MetadataBar/MetadataBar.stories.mdx
 create mode 100644 superset-frontend/src/components/MetadataBar/MetadataBarOverview.stories.tsx
 rename superset-frontend/src/components/Table/{Table.overview.mdx => TableOverview.stories.tsx} (67%)
 delete mode 100644 superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx
 copy superset-frontend/src/components/{RefreshLabel/RefreshLabel.stories.tsx => Table/cell-renderers/ActionCell/ActionCell.overview.tsx} (55%)
 delete mode 100644 superset-frontend/src/features/alerts/AlertReportModal.test.jsx
 create mode 100644 superset-frontend/src/features/alerts/buildErrorTooltipMessage.test.tsx
 copy superset-frontend/src/{explore/components/controls/BoundsControl.stories.tsx => features/alerts/buildErrorTooltipMessage.tsx} (51%)
 delete mode 100644 superset-frontend/src/features/alerts/components/AlertReportCronScheduler.test.tsx
 copy superset-frontend/src/{components/Switch/Switch.stories.tsx => features/alerts/components/NumberInput.tsx} (53%)
 create mode 100644 superset-frontend/src/features/alerts/components/StyledPanel.tsx
 copy superset-frontend/src/{components/FormRow/FormRow.stories.tsx => features/alerts/components/ValidatedPanelHeader.tsx} (50%)
 create mode 100644 superset-frontend/src/features/databases/DatabaseModal/SSHTunnelSwitch.test.tsx
 create mode 100644 superset/migrations/versions/2024-02-14_14-43_17fcea065655_change_text_to_mediumtext.py


(superset) 01/10: Remove tunnel via database update

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 8e9b13dd0461be8589fece8c091d5aa06c3f87a1
Author: geido <di...@gmail.com>
AuthorDate: Fri Feb 16 16:10:11 2024 +0200

    Remove tunnel via database update
---
 .../src/features/databases/DatabaseModal/index.tsx | 33 ++++--------
 superset-frontend/src/features/databases/types.ts  |  6 +--
 superset/commands/database/update.py               | 58 +++++++++++++++-------
 3 files changed, 52 insertions(+), 45 deletions(-)

diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
index 98e7cab415..46b090f465 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
@@ -596,7 +596,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
   const SSHTunnelSwitchComponent =
     extensionsRegistry.get('ssh_tunnel.form.switch') ?? SSHTunnelSwitch;
 
-  const [useSSHTunneling, setUseSSHTunneling] = useState<boolean>(false);
+  const [useSSHTunneling, setUseSSHTunneling] = useState<boolean | undefined>(
+    undefined,
+  );
 
   let dbConfigExtraExtension = extensionsRegistry.get(
     'databaseconnection.extraOption',
@@ -724,7 +726,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
     setSSHTunnelPrivateKeys({});
     setSSHTunnelPrivateKeyPasswords({});
     setConfirmedOverwrite(false);
-    setUseSSHTunneling(false);
+    setUseSSHTunneling(undefined);
     onHide();
   };
 
@@ -850,27 +852,10 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
 
     setLoading(true);
 
-    // tunnel config is kept until db is saved for UX convenience (toggling on and off)
     // strictly checking for false as an indication that the toggle got unchecked
-    if (isSSHTunneling && dbToUpdate?.parameters?.ssh === false) {
-      if (db?.id && dbFetched?.ssh_tunnel) {
-        // the db and ssh tunnel exist, should be removed
-        try {
-          await SupersetClient.delete({
-            endpoint: `/api/v1/database/${db.id}/ssh_tunnel/`,
-          });
-        } catch (e) {
-          addDangerToast(
-            t('There was an error removing the SSH tunnel configuration'),
-          );
-          setLoading(false);
-          console.error(e);
-          return;
-        }
-      }
-      handleClearSSHTunnelConfig();
-      // remove ssh tunnel from payload
-      dbToUpdate.ssh_tunnel = undefined;
+    if (isSSHTunneling && useSSHTunneling === false) {
+      // remove ssh tunnel
+      dbToUpdate.ssh_tunnel = null;
     }
 
     if (db?.id) {
@@ -1325,8 +1310,8 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
   }, [sshPrivateKeyPasswordNeeded]);
 
   useEffect(() => {
-    if (isSSHTunneling) {
-      setUseSSHTunneling(!!db?.parameters?.ssh);
+    if (isSSHTunneling && db?.parameters?.ssh !== undefined) {
+      setUseSSHTunneling(db?.parameters?.ssh);
       handleClearValidationErrors();
     }
   }, [db?.parameters?.ssh]);
diff --git a/superset-frontend/src/features/databases/types.ts b/superset-frontend/src/features/databases/types.ts
index dbbd661890..2f481d8315 100644
--- a/superset-frontend/src/features/databases/types.ts
+++ b/superset-frontend/src/features/databases/types.ts
@@ -112,7 +112,7 @@ export type DatabaseObject = {
   };
 
   // SSH Tunnel information
-  ssh_tunnel?: SSHTunnelObject;
+  ssh_tunnel?: SSHTunnelObject | null;
 };
 
 export type DatabaseForm = {
@@ -236,8 +236,8 @@ export interface ExtraJson {
   version?: string;
 }
 
-type ParametersChangeValueType = HTMLInputElement & {
-  value: string | boolean;
+type ParametersChangeValueType = Partial<Omit<HTMLInputElement, 'value'>> & {
+  value?: string | boolean;
 };
 
 type ParametersChangeType<T = ParametersChangeValueType> =
diff --git a/superset/commands/database/update.py b/superset/commands/database/update.py
index edc0ba1b98..b891c8f157 100644
--- a/superset/commands/database/update.py
+++ b/superset/commands/database/update.py
@@ -30,8 +30,10 @@ from superset.commands.database.exceptions import (
     DatabaseUpdateFailedError,
 )
 from superset.commands.database.ssh_tunnel.create import CreateSSHTunnelCommand
+from superset.commands.database.ssh_tunnel.delete import DeleteSSHTunnelCommand
 from superset.commands.database.ssh_tunnel.exceptions import (
     SSHTunnelCreateFailedError,
+    SSHTunnelDeleteFailedError,
     SSHTunnelingNotEnabledError,
     SSHTunnelInvalidError,
     SSHTunnelUpdateFailedError,
@@ -70,32 +72,52 @@ class UpdateDatabaseCommand(BaseCommand):
             database = DatabaseDAO.update(self._model, self._properties, commit=False)
             database.set_sqlalchemy_uri(database.sqlalchemy_uri)
 
-            if ssh_tunnel_properties := self._properties.get("ssh_tunnel"):
+            existing_ssh_tunnel_model = DatabaseDAO.get_ssh_tunnel(database.id)
+
+            if "ssh_tunnel" in self._properties:
                 if not is_feature_enabled("SSH_TUNNELING"):
                     db.session.rollback()
                     raise SSHTunnelingNotEnabledError()
-                existing_ssh_tunnel_model = DatabaseDAO.get_ssh_tunnel(database.id)
-                if existing_ssh_tunnel_model is None:
-                    # We couldn't found an existing tunnel so we need to create one
-                    try:
-                        CreateSSHTunnelCommand(database, ssh_tunnel_properties).run()
-                    except (SSHTunnelInvalidError, SSHTunnelCreateFailedError) as ex:
-                        # So we can show the original message
-                        raise ex
-                    except Exception as ex:
-                        raise DatabaseUpdateFailedError() from ex
-                else:
-                    # We found an existing tunnel so we need to update it
+
+                if not self._properties.get("ssh_tunnel") and existing_ssh_tunnel_model:
+                    # We need to remove the existing tunnel
                     try:
-                        UpdateSSHTunnelCommand(
-                            existing_ssh_tunnel_model.id, ssh_tunnel_properties
-                        ).run()
-                    except (SSHTunnelInvalidError, SSHTunnelUpdateFailedError) as ex:
-                        # So we can show the original message
+                        DeleteSSHTunnelCommand(existing_ssh_tunnel_model.id).run()
+                    except SSHTunnelDeleteFailedError as ex:
                         raise ex
                     except Exception as ex:
                         raise DatabaseUpdateFailedError() from ex
 
+                if ssh_tunnel_properties := self._properties.get("ssh_tunnel"):
+                    if existing_ssh_tunnel_model is None:
+                        # We couldn't found an existing tunnel so we need to create one
+                        try:
+                            CreateSSHTunnelCommand(
+                                database, ssh_tunnel_properties
+                            ).run()
+                        except (
+                            SSHTunnelInvalidError,
+                            SSHTunnelCreateFailedError,
+                        ) as ex:
+                            # So we can show the original message
+                            raise ex
+                        except Exception as ex:
+                            raise DatabaseUpdateFailedError() from ex
+                    else:
+                        # We found an existing tunnel so we need to update it
+                        try:
+                            UpdateSSHTunnelCommand(
+                                existing_ssh_tunnel_model.id, ssh_tunnel_properties
+                            ).run()
+                        except (
+                            SSHTunnelInvalidError,
+                            SSHTunnelUpdateFailedError,
+                        ) as ex:
+                            # So we can show the original message
+                            raise ex
+                        except Exception as ex:
+                            raise DatabaseUpdateFailedError() from ex
+
             # adding a new database we always want to force refresh schema list
             # TODO Improve this simplistic implementation for catching DB conn fails
             try:


(superset) 09/10: Add SSHTunnelSwitch test

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 5f37e1f54b25fb9413d2bc5a01b02a4f1d913bec
Author: geido <di...@gmail.com>
AuthorDate: Tue Feb 20 19:46:17 2024 +0200

    Add SSHTunnelSwitch test
---
 .../DatabaseModal/SSHTunnelSwitch.test.tsx         | 133 +++++++++++++++++++++
 .../databases/DatabaseModal/index.test.tsx         |  11 +-
 2 files changed, 140 insertions(+), 4 deletions(-)

diff --git a/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelSwitch.test.tsx b/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelSwitch.test.tsx
new file mode 100644
index 0000000000..dff05af9aa
--- /dev/null
+++ b/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelSwitch.test.tsx
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import SSHTunnelSwitch from './SSHTunnelSwitch';
+import { DatabaseObject } from '../types';
+
+jest.mock('src/components', () => ({
+  AntdSwitch: ({
+    checked,
+    onChange,
+  }: {
+    checked: boolean;
+    onChange: (checked: boolean) => void;
+  }) => (
+    <button
+      onClick={() => onChange(!checked)}
+      aria-checked={checked}
+      role="switch"
+      type="button"
+    >
+      {checked ? 'ON' : 'OFF'}
+    </button>
+  ),
+}));
+
+const mockChangeMethods = {
+  onParametersChange: jest.fn(),
+};
+
+const defaultDb = {
+  parameters: { ssh: false },
+  ssh_tunnel: {},
+} as DatabaseObject;
+
+test('Renders SSH Tunnel switch enabled by default and toggles its state', () => {
+  render(
+    <SSHTunnelSwitch
+      isSSHTunnelEnabled
+      changeMethods={mockChangeMethods}
+      db={defaultDb}
+    />,
+  );
+  const switchButton = screen.getByRole('switch');
+  expect(switchButton).toHaveTextContent('OFF');
+  userEvent.click(switchButton);
+  expect(mockChangeMethods.onParametersChange).toHaveBeenCalledWith({
+    target: { type: 'toggle', name: 'ssh', checked: true, value: true },
+  });
+  expect(switchButton).toHaveTextContent('ON');
+});
+
+test('Does not render if SSH Tunnel is disabled', () => {
+  render(
+    <SSHTunnelSwitch
+      isSSHTunnelEnabled={false}
+      changeMethods={mockChangeMethods}
+      db={defaultDb}
+    />,
+  );
+  expect(screen.queryByRole('switch')).not.toBeInTheDocument();
+});
+
+test('Checks the switch based on db.parameters.ssh', () => {
+  const dbWithSSHTunnelEnabled = {
+    ...defaultDb,
+    parameters: { ssh: true },
+  } as DatabaseObject;
+  render(
+    <SSHTunnelSwitch
+      isSSHTunnelEnabled
+      changeMethods={mockChangeMethods}
+      db={dbWithSSHTunnelEnabled}
+    />,
+  );
+  expect(screen.getByRole('switch')).toHaveTextContent('ON');
+});
+
+test('Calls onParametersChange with true if SSH Tunnel info exists', () => {
+  const dbWithSSHTunnelInfo = {
+    ...defaultDb,
+    ssh_tunnel: { host: 'example.com' },
+  } as DatabaseObject;
+  render(
+    <SSHTunnelSwitch
+      isSSHTunnelEnabled
+      changeMethods={mockChangeMethods}
+      db={dbWithSSHTunnelInfo}
+    />,
+  );
+  expect(mockChangeMethods.onParametersChange).toHaveBeenCalledWith({
+    target: { type: 'toggle', name: 'ssh', checked: true, value: true },
+  });
+});
+
+test('Displays tooltip text on hover over the InfoTooltip', async () => {
+  const tooltipText = 'SSH Tunnel configuration parameters';
+  render(
+    <SSHTunnelSwitch
+      isSSHTunnelEnabled
+      changeMethods={mockChangeMethods}
+      db={defaultDb}
+    />,
+  );
+
+  const infoTooltipTrigger = screen.getByRole('img', {
+    name: 'info-solid_small',
+  });
+  expect(infoTooltipTrigger).toBeInTheDocument();
+
+  userEvent.hover(infoTooltipTrigger);
+
+  const tooltip = await screen.findByText(tooltipText);
+
+  expect(tooltip).toBeInTheDocument();
+});
diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx
index 0f60857f06..7e8018b25f 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx
@@ -16,6 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+// TODO: These tests should be made atomic in separate files
+
 import React from 'react';
 import fetchMock from 'fetch-mock';
 import userEvent from '@testing-library/user-event';
@@ -1227,9 +1230,9 @@ describe('DatabaseModal', () => {
           const SSHTunnelServerPortInput = screen.getByTestId(
             'ssh-tunnel-server_port-input',
           );
-          expect(SSHTunnelServerPortInput).toHaveValue('');
+          expect(SSHTunnelServerPortInput).toHaveValue(null);
           userEvent.type(SSHTunnelServerPortInput, '22');
-          expect(SSHTunnelServerPortInput).toHaveValue('22');
+          expect(SSHTunnelServerPortInput).toHaveValue(22);
           const SSHTunnelUsernameInput = screen.getByTestId(
             'ssh-tunnel-username-input',
           );
@@ -1263,9 +1266,9 @@ describe('DatabaseModal', () => {
           const SSHTunnelServerPortInput = screen.getByTestId(
             'ssh-tunnel-server_port-input',
           );
-          expect(SSHTunnelServerPortInput).toHaveValue('');
+          expect(SSHTunnelServerPortInput).toHaveValue(null);
           userEvent.type(SSHTunnelServerPortInput, '22');
-          expect(SSHTunnelServerPortInput).toHaveValue('22');
+          expect(SSHTunnelServerPortInput).toHaveValue(22);
           const SSHTunnelUsernameInput = screen.getByTestId(
             'ssh-tunnel-username-input',
           );


(superset) 04/10: Validate inputs

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit a17aac711998b1227d72ba7cda36da1fa2dc4912
Author: geido <di...@gmail.com>
AuthorDate: Mon Feb 19 17:02:50 2024 +0200

    Validate inputs
---
 .../databases/DatabaseModal/SSHTunnelForm.tsx      | 12 ++++-----
 .../src/features/databases/DatabaseModal/index.tsx | 21 +++++++--------
 superset-frontend/src/features/databases/types.ts  | 30 +++++++++++++++++-----
 superset/commands/database/ssh_tunnel/create.py    |  3 +++
 superset/commands/database/ssh_tunnel/update.py    |  9 +++++++
 5 files changed, 49 insertions(+), 26 deletions(-)

diff --git a/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx b/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx
index 7823d82faf..e0d1b16ff2 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { EventHandler, ChangeEvent, useState } from 'react';
+import React, { useState } from 'react';
 import { t, styled } from '@superset-ui/core';
 import { AntdForm, Col, Row } from 'src/components';
 import { Form, FormLabel } from 'src/components/Form';
@@ -24,7 +24,7 @@ import { Radio } from 'src/components/Radio';
 import { Input, TextArea } from 'src/components/Input';
 import { Input as AntdInput, Tooltip } from 'antd';
 import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons';
-import { DatabaseObject } from '../types';
+import { DatabaseObject, FieldPropTypes } from '../types';
 import { AuthType } from '.';
 
 const StyledDiv = styled.div`
@@ -54,9 +54,7 @@ const SSHTunnelForm = ({
   setSSHTunnelLoginMethod,
 }: {
   db: DatabaseObject | null;
-  onSSHTunnelParametersChange: EventHandler<
-    ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
-  >;
+  onSSHTunnelParametersChange: FieldPropTypes['changeMethods']['onSSHTunnelParametersChange'];
   setSSHTunnelLoginMethod: (method: AuthType) => void;
 }) => {
   const [usePassword, setUsePassword] = useState<AuthType>(AuthType.Password);
@@ -86,9 +84,9 @@ const SSHTunnelForm = ({
             </FormLabel>
             <Input
               name="server_port"
-              type="text"
               placeholder={t('22')}
-              value={db?.ssh_tunnel?.server_port || ''}
+              type="number"
+              value={db?.ssh_tunnel?.server_port}
               onChange={onSSHTunnelParametersChange}
               data-test="ssh-tunnel-server_port-input"
             />
diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
index a4a7ddad4a..3607e5e400 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
@@ -662,7 +662,12 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
       masked_encrypted_extra: db?.masked_encrypted_extra || '',
       server_cert: db?.server_cert || undefined,
       ssh_tunnel:
-        !isEmpty(db?.ssh_tunnel) && useSSHTunneling ? db.ssh_tunnel : undefined,
+        !isEmpty(db?.ssh_tunnel) && useSSHTunneling
+          ? {
+              ...db.ssh_tunnel,
+              server_port: Number(db.ssh_tunnel!.server_port),
+            }
+          : undefined,
     };
     setTestInProgress(true);
     testDatabaseConnection(
@@ -694,10 +699,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
     setValidationErrors(null);
   };
 
-  const handleClearSSHTunnelConfig = () => {
-    setDB({ type: ActionType.RemoveSSHTunnelConfig });
-  };
-
   const handleParametersChange = ({ target }: { target: HTMLInputElement }) => {
     onChange(ActionType.ParametersChange, {
       type: target.type,
@@ -1541,8 +1542,8 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
         typeof dbErrors === 'object'
           ? Object.values(dbErrors)
           : typeof dbErrors === 'string'
-            ? [dbErrors]
-            : [];
+          ? [dbErrors]
+          : [];
     } else if (
       !isEmpty(validationErrors) &&
       validationErrors?.error_type === 'GENERIC_DB_ENGINE_ERROR'
@@ -1578,11 +1579,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
   const renderSSHTunnelForm = () => (
     <SSHTunnelForm
       db={db as DatabaseObject}
-      onSSHTunnelParametersChange={({
-        target,
-      }: {
-        target: HTMLInputElement | HTMLTextAreaElement;
-      }) =>
+      onSSHTunnelParametersChange={({ target }) =>
         onChange(ActionType.ParametersSSHTunnelChange, {
           type: target.type,
           name: target.name,
diff --git a/superset-frontend/src/features/databases/types.ts b/superset-frontend/src/features/databases/types.ts
index 2f481d8315..1ae4a7f715 100644
--- a/superset-frontend/src/features/databases/types.ts
+++ b/superset-frontend/src/features/databases/types.ts
@@ -1,6 +1,6 @@
 import { JsonObject } from '@superset-ui/core';
 import { InputProps } from 'antd/lib/input';
-import { FormEvent } from 'react';
+import { ChangeEvent, EventHandler, FormEvent } from 'react';
 
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -236,23 +236,39 @@ export interface ExtraJson {
   version?: string;
 }
 
-type ParametersChangeValueType = Partial<Omit<HTMLInputElement, 'value'>> & {
-  value?: string | boolean;
+type CustomTextType = {
+  value?: string | boolean | number;
+  type?: string | null;
+  name?: string;
+  checked?: boolean;
 };
 
-type ParametersChangeType<T = ParametersChangeValueType> =
+type CustomHTMLInputElement = Omit<Partial<CustomTextType>, 'value' | 'type'> &
+  CustomTextType;
+
+type CustomHTMLTextAreaElement = Omit<
+  Partial<CustomTextType>,
+  'value' | 'type'
+> &
+  CustomTextType;
+
+export type CustomParametersChangeType<T = CustomTextType> =
   | FormEvent<InputProps>
   | { target: T };
 
+export type CustomEventHandlerType = EventHandler<
+  ChangeEvent<CustomHTMLInputElement | CustomHTMLTextAreaElement>
+>;
+
 export interface FieldPropTypes {
   required: boolean;
   hasTooltip?: boolean;
   tooltipText?: (value: any) => string;
   placeholder?: string;
-  onParametersChange: (event: ParametersChangeType) => void;
+  onParametersChange: (event: CustomParametersChangeType) => void;
   onParametersUploadFileChange: (value: any) => string;
   changeMethods: {
-    onParametersChange: (event: ParametersChangeType) => void;
+    onParametersChange: (event: CustomParametersChangeType) => void;
   } & {
     onChange: (value: any) => string;
   } & {
@@ -262,7 +278,7 @@ export interface FieldPropTypes {
     onRemoveTableCatalog: (idx: number) => void;
   } & {
     onExtraInputChange: (value: any) => void;
-    onSSHTunnelParametersChange: (value: any) => string;
+    onSSHTunnelParametersChange: CustomEventHandlerType;
   };
   validationErrors: JsonObject | null;
   getValidation: () => void;
diff --git a/superset/commands/database/ssh_tunnel/create.py b/superset/commands/database/ssh_tunnel/create.py
index cbfee3ce2a..59e083d4d8 100644
--- a/superset/commands/database/ssh_tunnel/create.py
+++ b/superset/commands/database/ssh_tunnel/create.py
@@ -57,6 +57,7 @@ class CreateSSHTunnelCommand(BaseCommand):
         server_address: Optional[str] = self._properties.get("server_address")
         server_port: Optional[int] = self._properties.get("server_port")
         username: Optional[str] = self._properties.get("username")
+        password: Optional[str] = self._properties.get("password")
         private_key: Optional[str] = self._properties.get("private_key")
         private_key_password: Optional[str] = self._properties.get(
             "private_key_password"
@@ -67,6 +68,8 @@ class CreateSSHTunnelCommand(BaseCommand):
             exceptions.append(SSHTunnelRequiredFieldValidationError("server_port"))
         if not username:
             exceptions.append(SSHTunnelRequiredFieldValidationError("username"))
+        if not private_key and not password:
+            exceptions.append(SSHTunnelRequiredFieldValidationError("password"))
         if private_key_password and private_key is None:
             exceptions.append(SSHTunnelRequiredFieldValidationError("private_key"))
         if exceptions:
diff --git a/superset/commands/database/ssh_tunnel/update.py b/superset/commands/database/ssh_tunnel/update.py
index ae7ee78afe..47f7d4947a 100644
--- a/superset/commands/database/ssh_tunnel/update.py
+++ b/superset/commands/database/ssh_tunnel/update.py
@@ -43,6 +43,15 @@ class UpdateSSHTunnelCommand(BaseCommand):
         self.validate()
         try:
             if self._model is not None:  # So we dont get incompatible types error
+                # unset password if private key is provided
+                if self._properties.get("private_key"):
+                    self._properties["password"] = None
+
+                # unset private key and password if password is provided
+                if self._properties.get("password"):
+                    self._properties["private_key"] = None
+                    self._properties["private_key_password"] = None
+
                 tunnel = SSHTunnelDAO.update(self._model, self._properties)
         except DAOUpdateFailedError as ex:
             raise SSHTunnelUpdateFailedError() from ex


(superset) 03/10: Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 977f8f16b4b0dcdb89fdc0dcca4155a6e937c30a
Merge: 0f73196d7e cf33a6213d
Author: geido <di...@gmail.com>
AuthorDate: Fri Feb 16 16:17:01 2024 +0200

    Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

 .github/workflows/dependency-review.yml            |     2 +
 .gitignore                                         |     1 +
 .pre-commit-config.yaml                            |     2 +-
 .pylintrc                                          |     2 +-
 RESOURCES/INTHEWILD.md                             |     1 +
 UPDATING.md                                        |     4 +
 docs/docs/databases/firebolt.mdx                   |    11 +-
 .../docs/databases/installing-database-drivers.mdx |     2 +-
 .../importing-exporting-datasources.mdx            |     2 +-
 requirements/base.txt                              |     6 +-
 requirements/docker.txt                            |     2 +-
 requirements/testing.txt                           |     4 +-
 scripts/build_docker.py                            |     1 +
 setup.py                                           |     6 +-
 superset-frontend/.eslintrc.js                     |    63 +-
 superset-frontend/.storybook/main.js               |    33 +-
 superset-frontend/.storybook/preview.jsx           |    12 +-
 superset-frontend/cypress-base/cypress.config.ts   |     4 +
 .../cypress-base/cypress/e2e/sqllab/query.test.ts  |     3 -
 superset-frontend/package-lock.json                | 48993 ++++++++++---------
 superset-frontend/package.json                     |    62 +-
 .../src/components/Select.tsx                      |     4 +-
 .../superset-ui-chart-controls/src/index.ts        |     1 +
 .../src/operators/contributionOperator.ts          |    25 +-
 .../src/operators/timeCompareOperator.ts           |    41 +-
 .../src/operators/timeComparePivotOperator.ts      |    51 +-
 .../superset-ui-chart-controls/src/types.ts        |    70 +-
 .../src/chart/components/ChartDataProvider.tsx     |     2 +-
 .../src/chart/components/SuperChart.tsx            |     1 -
 .../src/chart/components/createLoadableRenderer.ts |     4 +-
 .../src/chart/models/ChartPlugin.ts                |     2 +-
 .../src/connection/callApi/parseResponse.ts        |     8 +-
 .../src/number-format/NumberFormats.ts             |     6 +-
 .../superset-ui-core/src/query/api/v1/makeApi.ts   |     9 +-
 .../superset-ui-core/src/query/api/v1/types.ts     |     4 +-
 .../superset-ui-core/src/query/types/Operator.ts   |     6 +-
 .../test/chart/components/MockChartPlugins.tsx     |     4 +-
 .../packages/superset-ui-demo/.babelrc.json        |    18 +
 .../packages/superset-ui-demo/.storybook/main.js   |    30 +-
 .../superset-ui-demo/.storybook/preview.js         |    20 +-
 .../superset-ui-demo/.storybook/storybook.css      |     6 +-
 .../packages/superset-ui-demo/package.json         |    21 +-
 .../shared/components/ResizableChartDemo.tsx       |     4 +-
 .../storybook/shared/components/ResizablePanel.tsx |     6 +-
 ...{createQueryStory.tsx => createQuery.story.tsx} |    39 +-
 ...s.tsx => LegacyPluginChartCalendar.stories.tsx} |     0
 ...ries.tsx => LegacyPluginChartChord.stories.tsx} |     0
 ...ountryMapStories.tsx => CountryMap.stories.tsx} |    49 +-
 .../{Stories.jsx => EventFlow.stories.jsx}         |     0
 .../{Stories.tsx => Heatmap.stories.tsx}           |     0
 .../{Stories.tsx => Histogram.stories.tsx}         |     0
 .../{Stories.tsx => Horizon.stories.tsx}           |     0
 .../{Stories.tsx => MapBox.stories.tsx}            |     0
 .../{Stories.tsx => TTest.stories.tsx}             |     0
 ...Stories.tsx => ParallelCoordinates.stories.tsx} |     0
 .../{Stories.tsx => Partition.stories.tsx}         |     0
 .../{Stories.tsx => Rose.stories.tsx}              |     0
 .../{Stories.tsx => SankeyLoop.stories.tsx}        |     0
 .../{Stories.tsx => Sankey.stories.tsx}            |     0
 .../{Stories.tsx => WorldMap.stories.tsx}          |     0
 ...{BigNumberStories.tsx => BigNumber.stories.tsx} |     0
 ...TotalStories.tsx => BigNumberTotal.stories.tsx} |     0
 .../Arc/{Stories.tsx => Arc.stories.tsx}           |     0
 .../Grid/{Stories.tsx => Grid.stories.tsx}         |     0
 .../Hex/{Stories.tsx => Hex.stories.tsx}           |     0
 .../Path/{Stories.tsx => Path.stories.tsx}         |     0
 .../Polygon/{Stories.tsx => Polygon.stories.tsx}   |     0
 .../Scatter/{Stories.tsx => Scatter.stories.tsx}   |     0
 .../Area/{Stories.tsx => NVD3Area.stories.tsx}     |     0
 .../Area/stories/stackedWithBounds.tsx             |     8 +-
 .../Bar/{Stories.tsx => NVD3Bar.stories.tsx}       |     0
 .../{Stories.tsx => NVD3BoxPlot.stories.tsx}       |     0
 .../Bubble/{Stories.tsx => NVD3Bubble.stories.tsx} |     0
 .../Bullet/{Stories.tsx => NVD3Bullet.stories.tsx} |     0
 .../{Stories.tsx => NVD3Compare.stories.tsx}       |     0
 .../{Stories.tsx => NVD3DistBar.stories.tsx}       |     0
 .../Line/{Stories.tsx => NVD3Line.stories.tsx}     |     0
 .../Pie/{Stories.tsx => NVD3Pie.stories.tsx}       |     0
 .../AreaSeries/AreaSeries.stories.tsx              |   198 +
 .../{AreaTimeseries => AreaSeries}/data.ts         |     0
 .../AreaTimeseries/Stories.tsx                     |    88 -
 .../BoxPlot/{Stories.tsx => BoxPolot.stories.tsx}  |    28 +-
 .../{Stories.tsx => BubbleChart.stories.tsx}       |    95 +-
 .../Funnel/{Stories.tsx => Funnel.stories.tsx}     |    79 +-
 .../Gauge/{Stories.tsx => Gauge.stories.tsx}       |     3 +-
 .../Graph/{Stories.tsx => Graph.stories.tsx}       |     3 +-
 .../MixedSeries/MixedSeries.stories.tsx            |   352 +
 .../negativeData.ts                                |     0
 .../MixedTimeseries/Stories.tsx                    |   173 -
 .../plugin-chart-echarts/Pie/Pie.stories.tsx       |   195 +
 .../plugins/plugin-chart-echarts/Pie/Stories.tsx   |   108 -
 .../Radar/{Stories.tsx => Radar.stories.tsx}       |     3 +-
 .../Sunburst/{Stories.tsx => Sunburst.stories.tsx} |    26 +-
 .../{Stories.tsx => Timeseries.stories.tsx}        |   149 +-
 .../Tree/{Stories.tsx => Tree.stories.tsx}         |   126 +-
 .../Treemap/{Stories.tsx => Treemap.stories.tsx}   |    41 +-
 .../{Stories.tsx => Waterfall.stories.tsx}         |     5 +-
 ...ivotTableStories.tsx => PivotTable.stories.tsx} |    25 +-
 .../{TableStories.tsx => Table.stories.tsx}        |   101 +-
 .../{Stories.tsx => WordCloud.stories.tsx}         |    56 +-
 ...erStories.tsx => ChartDataProvider.stories.tsx} |   128 +-
 .../superset-ui-chart/SuperChart.stories.tsx       |   255 +
 .../superset-ui-chart/SuperChartStories.tsx        |   169 -
 ...lettesStories.tsx => ColorPalettes.stories.tsx} |     0
 ...onnectionStories.tsx => Connection.stories.tsx} |    69 +-
 ...gNumberStories.tsx => NumberFormat.stories.tsx} |     0
 .../{ThemeStories.tsx => Theme.stories.tsx}        |     0
 ...imeFormatStories.tsx => TimeFormat.stories.tsx} |     0
 .../superset-ui-switchboard/src/switchboard.ts     |     2 +-
 .../legacy-plugin-chart-heatmap/src/Heatmap.js     |     2 +-
 .../src/MixedTimeseries/transformProps.ts          |    23 +-
 .../src/Timeseries/constants.ts                    |    25 +-
 .../src/Timeseries/transformProps.ts               |     8 +-
 .../src/Waterfall/transformProps.ts                |     6 +-
 .../plugin-chart-echarts/src/utils/forecast.ts     |    15 +-
 .../plugin-chart-echarts/src/utils/formatters.ts   |    11 +-
 .../test/utils/formatters.test.ts}                 |    37 +-
 .../src/PopKPI.tsx                                 |    46 +-
 .../src/plugin/controlPanel.ts                     |    12 +-
 .../src/plugin/transformProps.ts                   |    14 +
 .../src/types.ts                                   |     1 +
 .../src/utils.ts                                   |    45 +-
 .../src/react-pivottable/TableRenderers.jsx        |     6 +-
 .../src/react-pivottable/utilities.js              |     3 +-
 .../src/DataTable/hooks/useSticky.tsx              |     3 +-
 .../plugins/plugin-chart-table/src/TableChart.tsx  |     4 +-
 .../plugin-chart-table/src/transformProps.ts       |     3 +-
 .../plugin-chart-table/src/utils/formatValue.ts    |    10 +-
 .../src/chart/WordCloud.tsx                        |    11 +-
 .../SqlLab/components/AceEditorWrapper/index.tsx   |     4 +-
 .../components/AceEditorWrapper/useAnnotations.ts  |    18 +-
 .../src/SqlLab/components/App/App.test.tsx         |    20 +-
 .../src/SqlLab/components/App/index.tsx            |     3 +
 .../components/QueryHistory/QueryHistory.test.tsx  |     5 +-
 .../src/SqlLab/components/QueryHistory/index.tsx   |    29 +-
 .../QueryLimitSelect/QueryLimitSelect.test.tsx     |    12 +-
 .../src/SqlLab/components/QueryTable/index.tsx     |     3 +-
 .../SqlLab/components/ResultSet/ResultSet.test.tsx |   333 +-
 .../src/SqlLab/components/ResultSet/index.tsx      |    52 +-
 .../{SouthPane.test.tsx => Results.test.tsx}       |    87 +-
 .../src/SqlLab/components/SouthPane/Results.tsx    |   106 +
 .../SqlLab/components/SouthPane/SouthPane.test.tsx |    81 +-
 .../src/SqlLab/components/SouthPane/index.tsx      |   174 +-
 .../SqlLab/components/SqlEditor/SqlEditor.test.tsx |     7 +-
 .../src/SqlLab/components/SqlEditor/index.tsx      |    72 +-
 .../SqlLab/components/TabbedSqlEditors/index.tsx   |     1 -
 .../components/TableElement/TableElement.test.tsx  |     5 +-
 .../src/SqlLab/components/TableElement/index.tsx   |     4 +-
 .../src/SqlLab/reducers/getInitialState.ts         |     4 -
 .../src/SqlLab/utils/newQueryTabName.ts            |     4 +-
 superset-frontend/src/assets/staticPages/404.html  |     2 +-
 superset-frontend/src/assets/staticPages/500.html  |     2 +-
 .../src/assets/stylesheets/less/variables.less     |     8 +-
 .../src/components/Alert/Alert.stories.tsx         |    25 +-
 .../AlteredSliceTag/AlteredSliceTag.stories.tsx    |     8 -
 ...redSliceTagMocks.js => AlteredSliceTagMocks.ts} |    11 +-
 .../AlteredSliceTag/{index.jsx => index.tsx}       |   133 +-
 .../AsyncAceEditor/AsyncAceEditor.stories.tsx      |    11 +-
 .../AsyncEsmComponent.stories.tsx                  |     8 -
 .../src/components/Badge/Badge.stories.tsx         |    42 +-
 .../src/components/Button/Button.stories.tsx       |    25 +-
 .../components/ButtonGroup/ButtonGroup.stories.tsx |    11 +-
 .../src/components/Card/Card.stories.tsx           |     8 -
 .../CertifiedBadge/CertifiedBadge.stories.tsx      |     8 -
 .../src/components/Chart/ChartRenderer.jsx         |     2 +-
 .../src/components/Chart/DrillBy/DrillByModal.tsx  |     4 +-
 .../Chart/DrillBy/useDisplayModeToggle.tsx         |     4 +-
 .../DrillDetail/DrillDetailMenuItems.test.tsx      |     7 +-
 .../Chart/DrillDetail/DrillDetailPane.tsx          |    15 +-
 .../src/components/Collapse/Collapse.stories.tsx   |    11 +-
 .../ConfirmStatusChange.stories.tsx                |     8 -
 .../CopyToClipboard/CopyToClipboard.stories.tsx    |     8 -
 .../components/CronPicker/CronPicker.stories.tsx   |     8 -
 .../components/DatePicker/DatePicker.stories.tsx   |    16 -
 .../src/components/DesignSystem.stories.mdx        |    25 -
 .../src/components/DesignSystem.stories.tsx        |    54 +
 .../src/components/Dropdown/Dropdown.stories.tsx   |     8 -
 .../DropdownButton/DropdownButton.stories.tsx      |     8 -
 .../DropdownContainer/Overview.stories.mdx         |    17 -
 .../Overview.stories.tsx}                          |    49 +-
 .../DropdownSelectableIcon.stories.tsx             |     8 -
 .../components/DropdownSelectableIcon/index.tsx    |    33 +-
 .../EditableTitle/EditableTitle.stories.tsx        |     8 -
 .../ErrorMessage/BasicErrorAlert.test.tsx          |     5 +-
 .../ErrorMessage/DatabaseErrorMessage.test.tsx     |     5 +-
 .../DatasetNotFoundErrorMessage.test.tsx           |     5 +-
 .../components/ErrorMessage/ErrorAlert.test.tsx    |     5 +-
 .../ErrorMessageWithStackTrace.test.tsx            |     5 +-
 .../ErrorMessage/ParameterErrorMessage.test.tsx    |     5 +-
 .../ErrorMessage/TimeoutErrorMessage.test.tsx      |     5 +-
 .../src/components/FacePile/FacePile.stories.tsx   |     9 +-
 .../FilterableTable/FilterableTable.stories.tsx    |     8 -
 .../src/components/FormRow/FormRow.stories.tsx     |     8 -
 .../components/IconTooltip/IconTooltip.stories.tsx |     8 -
 .../src/components/Icons/Icons.stories.tsx         |     8 -
 .../IndeterminateCheckbox.stories.tsx              |     8 -
 .../components/InfoTooltip/InfoTooltip.stories.tsx |     8 -
 .../ListViewCard/ListViewCard.stories.tsx          |    41 +-
 .../src/components/Loading/Loading.stories.tsx     |    25 +-
 .../components/MessageToasts/ToastPresenter.tsx    |     3 +-
 .../getToastsFromPyFlashMessages.test.js           |    48 -
 .../components/MetadataBar/MetadataBar.stories.mdx |   145 -
 .../components/MetadataBar/MetadataBar.stories.tsx |     8 -
 .../MetadataBar/MetadataBarOverview.stories.tsx    |   177 +
 .../src/components/Modal/Modal.stories.tsx         |     8 -
 superset-frontend/src/components/Modal/Modal.tsx   |     5 +-
 .../PopoverDropdown/PopoverDropdown.stories.tsx    |     8 -
 .../PopoverSection/PopoverSection.stories.tsx      |     8 -
 .../RefreshLabel/RefreshLabel.stories.tsx          |     8 -
 .../src/components/Select/AsyncSelect.stories.tsx  |     8 -
 .../src/components/Select/AsyncSelect.test.tsx     |     4 +-
 .../src/components/Select/Select.stories.tsx       |    42 +-
 .../src/components/Slider/Slider.stories.tsx       |     8 -
 .../src/components/Switch/Switch.stories.tsx       |     8 -
 ...able.overview.mdx => TableOverview.stories.tsx} |   152 +-
 .../ActionCell/ActionCell.overview.mdx             |    69 -
 .../ActionCell/ActionCell.overview.tsx}            |    37 +-
 .../TableSelector/TableSelector.test.tsx           |     2 +-
 .../src/components/TableSelector/index.tsx         |     5 +-
 .../src/components/TableView/TableView.stories.tsx |     8 -
 .../src/components/Tabs/Tabs.stories.tsx           |     8 -
 .../src/components/Tags/TagsList.stories.tsx       |     8 -
 .../src/components/Timer/Timer.stories.tsx         |    11 +-
 .../src/components/TimezoneSelector/index.tsx      |     4 +-
 .../src/components/Tooltip/Tooltip.stories.tsx     |     8 -
 .../TooltipParagraph/TooltipParagraph.stories.tsx  |     8 -
 .../src/components/UiConfigContext/index.tsx       |    31 +-
 .../WarningIconWithTooltip.stories.tsx             |     8 -
 .../components/AnchorLink/AnchorLink.stories.tsx   |     8 -
 .../src/dashboard/components/Dashboard.jsx         |     2 -
 .../src/dashboard/components/Dashboard.test.jsx    |     1 -
 .../DashboardBuilder/DashboardBuilder.tsx          |    85 +-
 .../dashboard/components/DashboardGrid.test.jsx    |    21 +-
 .../components/FiltersBadge/FiltersBadge.test.tsx  |     5 +-
 .../SliceHeaderControls.test.tsx                   |     2 +-
 .../components/filterscope/FilterScope.test.tsx    |     4 +-
 .../components/gridComponents/Column.test.jsx      |    33 +-
 .../components/gridComponents/Row.test.jsx         |    33 +-
 .../components/gridComponents/Tabs.test.jsx        |    19 +-
 .../gridComponents/new/NewColumn.test.jsx          |     5 +-
 .../gridComponents/new/NewDivider.test.jsx         |     5 +-
 .../gridComponents/new/NewHeader.test.jsx          |     5 +-
 .../components/gridComponents/new/NewRow.test.jsx  |     5 +-
 .../components/gridComponents/new/NewTabs.test.jsx |     5 +-
 .../components/menu/BackgroundStyleDropdown.tsx    |     7 +-
 .../FilterBarSettings/FilterBarSettings.test.tsx   |     1 -
 .../FilterBar/FilterControls/FilterControls.tsx    |     4 +-
 .../FilterControls/FilterDivider.stories.tsx       |     8 -
 .../nativeFilters/FilterBar/Header/index.tsx       |     4 +-
 .../nativeFilters/FilterBar/Horizontal.tsx         |     4 +-
 .../nativeFilters/FilterCard/useFilterScope.ts     |     9 +-
 .../FiltersConfigForm/ColumnSelect.tsx             |     4 +-
 .../FiltersConfigForm/FilterScope/FilterScope.tsx  |     5 +-
 .../src/dashboard/containers/Dashboard.ts          |     1 -
 superset-frontend/src/dashboard/reducers/types.ts  |     2 +-
 superset-frontend/src/dashboard/types.ts           |     3 +-
 .../explore/components/ControlHeader.stories.tsx   |     8 -
 .../explore/components/ControlPanelsContainer.tsx  |     4 +-
 .../explore/components/DataTableControl/index.tsx  |     4 +-
 .../components/DataTablesPane/test/fixture.tsx     |     6 +-
 .../ExploreChartHeader/ExploreChartHeader.test.tsx |     5 +-
 .../PropertiesModal/PropertiesModal.test.tsx       |     2 +-
 .../RunQueryButton/RunQueryButton.stories.tsx      |     8 -
 .../components/controls/BoundsControl.stories.tsx  |     8 -
 .../ColorSchemeControl/ColorSchemeControl.test.tsx |     2 +-
 .../ColumnConfigControl/ColumnConfigPopover.tsx    |     4 +-
 .../ColumnConfigControl/ControlForm/index.tsx      |     2 +-
 .../controls/ColumnConfigControl/types.ts          |     2 +-
 .../DatasourceControl/DatasourceControl.test.tsx   |     4 +-
 .../DndColumnSelectControl/ColumnSelectPopover.tsx |     4 +-
 .../FixedOrMetricControl.test.tsx                  |     5 +-
 .../controls/MetricControl/AdhocMetric.js          |     4 +-
 .../MetricControl/AdhocMetricEditPopoverTitle.tsx  |   135 +-
 .../controls/VizTypeControl/FastVizSwitcher.tsx    |     3 +-
 .../src/features/alerts/AlertReportModal.tsx       |     8 +-
 .../alerts/components/AlertReportCronScheduler.tsx |   135 +-
 .../src/features/databases/DatabaseModal/index.tsx |     4 +-
 .../AddDataset/DatasetPanel/DatasetPanel.test.tsx  |     5 +-
 .../DatasetMetadataBar.skipped-stories.tsx         |     8 -
 .../features/queries/SavedQueryPreviewModal.tsx    |   133 +-
 .../src/features/rls/RowLevelSecurityModal.tsx     |    88 +-
 superset-frontend/src/features/rls/constants.ts    |     2 +-
 .../Select/SelectFilterPlugin.stories.tsx          |    14 +-
 superset-frontend/src/pages/Chart/Chart.test.tsx   |    11 +-
 .../src/pages/ExecutionLogList/index.tsx           |     4 +-
 superset-frontend/src/utils/urlUtils.ts            |     2 +-
 .../src/visualizations/TimeTable/transformProps.ts |    13 +-
 superset-frontend/webpack.config.js                |    33 +-
 .../advanced_data_type/plugins/internet_port.py    |    11 +-
 superset/cli/importexport.py                       |     3 +-
 superset/commands/chart/delete.py                  |     3 +-
 superset/commands/chart/importers/v1/__init__.py   |    10 +-
 superset/commands/chart/importers/v1/utils.py      |    13 +-
 superset/commands/dashboard/delete.py              |     3 +-
 .../commands/dashboard/importers/v1/__init__.py    |    19 +-
 superset/commands/dashboard/importers/v1/utils.py  |    11 +-
 superset/commands/database/delete.py               |     3 +-
 .../commands/database/importers/v1/__init__.py     |     8 +-
 superset/commands/database/importers/v1/utils.py   |    13 +-
 superset/commands/database/validate_sql.py         |    10 +-
 superset/commands/dataset/importers/v0.py          |    57 +-
 superset/commands/dataset/importers/v1/__init__.py |     8 +-
 superset/commands/dataset/importers/v1/utils.py    |    20 +-
 superset/commands/importers/v1/__init__.py         |     6 +-
 superset/commands/importers/v1/assets.py           |    21 +-
 superset/commands/importers/v1/examples.py         |    11 +-
 superset/commands/query/importers/v1/__init__.py   |     8 +-
 superset/commands/query/importers/v1/utils.py      |    13 +-
 superset/commands/report/alert.py                  |     8 +-
 superset/connectors/sqla/models.py                 |    17 +-
 superset/connectors/sqla/utils.py                  |     6 +-
 superset/daos/base.py                              |    13 +-
 superset/databases/api.py                          |    14 +-
 superset/databases/filters.py                      |     4 +-
 superset/db_engine_specs/gsheets.py                |     6 +-
 superset/extensions/__init__.py                    |     2 +-
 superset/initialization/__init__.py                |     2 -
 ...14-43_17fcea065655_change_text_to_mediumtext.py |    87 +
 superset/models/annotations.py                     |     5 +-
 superset/models/core.py                            |     4 +-
 superset/models/dashboard.py                       |     4 +-
 superset/models/helpers.py                         |    18 +-
 superset/models/slice.py                           |     4 +-
 superset/models/sql_lab.py                         |    12 +-
 superset/reports/models.py                         |    13 +-
 superset/security/manager.py                       |    16 +-
 superset/sqllab/schemas.py                         |     2 +-
 superset/tables/models.py                          |     2 +-
 superset/tags/models.py                            |    24 +-
 superset/translations/de/LC_MESSAGES/messages.json | 10142 ++--
 superset/translations/de/LC_MESSAGES/messages.po   | 33222 +++++++------
 superset/translations/en/LC_MESSAGES/messages.json |  7786 ++-
 superset/translations/en/LC_MESSAGES/messages.po   | 22882 +++++----
 superset/translations/es/LC_MESSAGES/messages.json |  6066 ++-
 superset/translations/es/LC_MESSAGES/messages.po   | 30539 ++++++------
 superset/translations/fr/LC_MESSAGES/messages.json |  7210 ++-
 superset/translations/fr/LC_MESSAGES/messages.po   | 32187 ++++++------
 superset/translations/it/LC_MESSAGES/messages.json |  6270 ++-
 superset/translations/it/LC_MESSAGES/messages.po   | 27830 +++++------
 superset/translations/ja/LC_MESSAGES/messages.json |  6299 ++-
 superset/translations/ja/LC_MESSAGES/messages.po   | 28673 +++++------
 superset/translations/ko/LC_MESSAGES/messages.json |  6363 ++-
 superset/translations/ko/LC_MESSAGES/messages.po   | 27202 +++++-----
 superset/translations/messages.pot                 | 22872 +++++----
 superset/translations/nl/LC_MESSAGES/messages.json |  7706 ++-
 superset/translations/nl/LC_MESSAGES/messages.po   | 28948 +++++------
 superset/translations/pt/LC_MESSAGES/messages.json |  6196 ++-
 superset/translations/pt/LC_MESSAGES/messages.po   | 28736 +++++------
 .../translations/pt_BR/LC_MESSAGES/messages.json   |  9781 ++--
 .../translations/pt_BR/LC_MESSAGES/messages.po     | 33114 +++++++------
 superset/translations/ru/LC_MESSAGES/messages.json |  9328 ++--
 superset/translations/ru/LC_MESSAGES/messages.po   | 32316 ++++++------
 superset/translations/sk/LC_MESSAGES/messages.json |  7714 ++-
 superset/translations/sk/LC_MESSAGES/messages.po   | 23418 +++++----
 superset/translations/sl/LC_MESSAGES/messages.json | 10412 ++--
 superset/translations/sl/LC_MESSAGES/messages.po   | 32495 ++++++------
 superset/translations/uk/LC_MESSAGES/messages.json | 10072 ++--
 superset/translations/uk/LC_MESSAGES/messages.po   | 24756 ++++++----
 superset/translations/zh/LC_MESSAGES/messages.json |  7451 ++-
 superset/translations/zh/LC_MESSAGES/messages.po   | 31489 ++++++------
 superset/utils/dashboard_import_export.py          |     7 +-
 superset/utils/dict_import_export.py               |     7 +-
 superset/views/api.py                              |     4 +-
 superset/views/core.py                             |    24 +-
 superset/views/sql_lab/views.py                    |    15 +-
 superset/viz.py                                    |     3 +-
 tests/integration_tests/base_tests.py              |    30 +-
 tests/integration_tests/cache_tests.py             |     4 +-
 tests/integration_tests/charts/api_tests.py        |    10 +-
 tests/integration_tests/charts/commands_tests.py   |     2 +-
 tests/integration_tests/core_tests.py              |    32 +-
 tests/integration_tests/dashboards/api_tests.py    |     4 +-
 .../dashboards/filter_state/api_tests.py           |     7 +-
 .../dashboards/permalink/api_tests.py              |     6 +-
 .../dashboards/superset_factory_util.py            |    58 +-
 tests/integration_tests/databases/api_tests.py     |    10 +-
 tests/integration_tests/datasets/api_tests.py      |     1 -
 tests/integration_tests/datasource_tests.py        |    36 +-
 .../db_engine_specs/databricks_tests.py            |    16 +-
 .../db_engine_specs/hive_tests.py                  |    14 +-
 .../db_engine_specs/postgres_tests.py              |    24 +-
 .../db_engine_specs/presto_tests.py                |    14 +-
 .../integration_tests/dict_import_export_tests.py  |    26 +-
 tests/integration_tests/explore/api_tests.py       |    10 +-
 .../explore/form_data/api_tests.py                 |    10 +-
 .../explore/form_data/commands_tests.py            |    32 +-
 .../explore/permalink/api_tests.py                 |     3 +-
 .../explore/permalink/commands_tests.py            |    32 +-
 tests/integration_tests/fixtures/datasource.py     |    13 +-
 tests/integration_tests/import_export_tests.py     |    15 +-
 .../key_value/commands/fixtures.py                 |     3 +-
 .../security/guest_token_security_tests.py         |    15 +-
 .../security/migrate_roles_tests.py                |     5 +-
 .../security/row_level_security_tests.py           |    29 +-
 tests/integration_tests/security_tests.py          |    40 +-
 tests/integration_tests/sqllab_tests.py            |     6 +-
 tests/integration_tests/test_jinja_context.py      |    38 +-
 tests/integration_tests/utils/get_dashboards.py    |     5 +-
 tests/integration_tests/utils_tests.py             |     4 +-
 .../charts/commands/importers/v1/import_test.py    |    12 +-
 tests/unit_tests/charts/dao/dao_tests.py           |    16 +-
 tests/unit_tests/charts/test_post_processing.py    |     7 +-
 tests/unit_tests/columns/test_models.py            |     7 +-
 .../commands/importers/v1/assets_test.py           |    34 +-
 tests/unit_tests/config_test.py                    |     4 +-
 tests/unit_tests/conftest.py                       |     4 +-
 tests/unit_tests/dao/dataset_test.py               |     5 +-
 tests/unit_tests/dao/queries_test.py               |    80 +-
 tests/unit_tests/dao/tag_test.py                   |     2 +-
 .../commands/importers/v1/import_test.py           |    14 +-
 tests/unit_tests/dashboards/dao_tests.py           |    12 +-
 tests/unit_tests/databases/api_test.py             |    50 +-
 .../databases/commands/importers/v1/import_test.py |    27 +-
 tests/unit_tests/databases/dao/dao_tests.py        |    10 +-
 .../databases/ssh_tunnel/commands/create_test.py   |    12 +-
 .../databases/ssh_tunnel/commands/delete_test.py   |    10 +-
 .../databases/ssh_tunnel/commands/update_test.py   |    10 +-
 tests/unit_tests/databases/ssh_tunnel/dao_tests.py |     4 +-
 tests/unit_tests/datasets/api_tests.py             |     8 +-
 tests/unit_tests/datasets/commands/export_test.py  |     8 +-
 .../datasets/commands/importers/v1/import_test.py  |    58 +-
 tests/unit_tests/datasets/dao/dao_tests.py         |    10 +-
 tests/unit_tests/datasource/dao_tests.py           |    14 +-
 tests/unit_tests/db_engine_specs/test_druid.py     |    16 +-
 tests/unit_tests/db_engine_specs/test_pinot.py     |     8 +-
 tests/unit_tests/extensions/test_sqlalchemy.py     |    27 +-
 tests/unit_tests/queries/dao_test.py               |     4 +-
 tests/unit_tests/scripts/docker_build.py           |     2 +-
 tests/unit_tests/sql_lab_test.py                   |    10 +-
 tests/unit_tests/sql_parse_tests.py                |     3 +-
 tests/unit_tests/tables/test_models.py             |    10 +-
 tests/unit_tests/tags/commands/create_test.py      |    29 +-
 tests/unit_tests/tags/commands/update_test.py      |    23 +-
 433 files changed, 323757 insertions(+), 313397 deletions(-)



(superset) 10/10: Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 311a31834004e64964f21698649b1055660381a7
Merge: 5f37e1f54b 3818da8509
Author: geido <di...@gmail.com>
AuthorDate: Wed Feb 21 12:13:05 2024 +0200

    Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

 .github/ISSUE_TEMPLATE/bug-report.yml              |    4 +-
 .github/workflows/superset-python-unittest.yml     |    4 +
 CHANGELOG.md                                       |    2 +
 CHANGELOG/3.0.4.md                                 |   72 ++
 CHANGELOG/3.1.1.md                                 |   75 ++
 RESOURCES/INTHEWILD.md                             |    1 +
 helm/superset/Chart.yaml                           |    2 +-
 helm/superset/README.md                            |   22 +-
 helm/superset/templates/_helpers.tpl               |   25 +
 helm/superset/templates/deployment-beat.yaml       |    3 +-
 helm/superset/templates/deployment-flower.yaml     |    3 +-
 helm/superset/templates/deployment-worker.yaml     |    3 +-
 helm/superset/templates/deployment-ws.yaml         |    3 +-
 helm/superset/templates/deployment.yaml            |    3 +-
 helm/superset/templates/pdb-beat.yaml              |   45 +
 helm/superset/templates/pdb-flower.yaml            |   45 +
 helm/superset/templates/pdb-worker.yaml            |   45 +
 helm/superset/templates/pdb-ws.yaml                |   45 +
 helm/superset/templates/pdb.yaml                   |   45 +
 helm/superset/values.yaml                          |   41 +-
 requirements/base.txt                              |   25 +-
 requirements/development.txt                       |    2 -
 requirements/integration.txt                       |    6 -
 requirements/testing.txt                           |    4 +-
 setup.py                                           |    3 +-
 superset-frontend/src/GlobalStyles.tsx             |    1 +
 superset-frontend/src/components/Button/index.tsx  |   11 +-
 .../src/components/CronPicker/CronPicker.tsx       |  115 +-
 superset-frontend/src/components/Modal/Modal.tsx   |    3 +
 .../src/features/alerts/AlertReportModal.test.jsx  |  367 ------
 .../src/features/alerts/AlertReportModal.test.tsx  |  632 +++++++++-
 .../src/features/alerts/AlertReportModal.tsx       | 1296 +++++++++++---------
 .../alerts/buildErrorTooltipMessage.test.tsx       |   70 ++
 .../features/alerts/buildErrorTooltipMessage.tsx   |   49 +
 .../components/AlertReportCronScheduler.test.tsx   |  153 ---
 .../alerts/components/AlertReportCronScheduler.tsx |   96 +-
 .../alerts/components/NotificationMethod.tsx       |   31 +-
 .../src/features/alerts/components/NumberInput.tsx |   52 +
 .../src/features/alerts/components/StyledPanel.tsx |   75 ++
 .../alerts/components/ValidatedPanelHeader.tsx     |   53 +
 superset-frontend/src/features/alerts/types.ts     |   18 +
 .../src/features/reports/ReportModal/index.tsx     |    9 +-
 superset/advanced_data_type/api.py                 |    4 +
 superset/commands/explore/parameters.py            |    3 -
 superset/config.py                                 |    2 +-
 superset/explore/api.py                            |    3 +-
 superset/sqllab/api.py                             |    4 +-
 superset/sqllab/execution_context_convertor.py     |    1 -
 tests/integration_tests/async_events/api_tests.py  |    4 +-
 tests/integration_tests/charts/data/api_tests.py   |    2 +-
 tests/unit_tests/security/api_test.py              |    6 +-
 51 files changed, 2297 insertions(+), 1291 deletions(-)


(superset) 05/10: Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 87fc78e12aecef065a2af7b0e21ce4672e41310a
Merge: a17aac7119 1776405903
Author: geido <di...@gmail.com>
AuthorDate: Mon Feb 19 17:04:03 2024 +0200

    Merge branch 'master' of https://github.com/apache/superset into diego/ch78628/fix-disabled-ssh-toggle

 .../src/plugin/buildQuery.ts                       | 10 ++++-
 .../ErrorBoundary/ErrorBoundary.test.tsx           |  8 ++--
 .../ErrorBoundary/{index.jsx => index.tsx}         | 49 +++++++++++++---------
 tests/integration_tests/base_tests.py              |  7 +---
 tests/integration_tests/core_tests.py              |  7 +---
 .../security/guest_token_security_tests.py         |  4 +-
 tests/integration_tests/security_tests.py          |  6 +--
 7 files changed, 48 insertions(+), 43 deletions(-)


(superset) 06/10: Catch missing database port for SSH Tunnel

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit b94994f07fd3a121cd17536dc610a3d48bc325ba
Author: geido <di...@gmail.com>
AuthorDate: Tue Feb 20 13:23:12 2024 +0200

    Catch missing database port for SSH Tunnel
---
 superset/commands/database/create.py               |  4 ++
 superset/commands/database/ssh_tunnel/create.py    |  8 ++++
 .../commands/database/ssh_tunnel/exceptions.py     |  4 ++
 superset/commands/database/ssh_tunnel/update.py    |  6 +++
 superset/commands/database/test_connection.py      | 52 ++++++++++++----------
 superset/commands/database/update.py               | 33 ++++++++------
 superset/databases/api.py                          |  5 ++-
 7 files changed, 73 insertions(+), 39 deletions(-)

diff --git a/superset/commands/database/create.py b/superset/commands/database/create.py
index cde9dd8e88..1ddc08e6a1 100644
--- a/superset/commands/database/create.py
+++ b/superset/commands/database/create.py
@@ -19,6 +19,7 @@ from typing import Any, Optional
 
 from flask import current_app
 from flask_appbuilder.models.sqla import Model
+from flask_babel import gettext as _
 from marshmallow import ValidationError
 
 from superset import is_feature_enabled
@@ -33,6 +34,7 @@ from superset.commands.database.exceptions import (
 from superset.commands.database.ssh_tunnel.create import CreateSSHTunnelCommand
 from superset.commands.database.ssh_tunnel.exceptions import (
     SSHTunnelCreateFailedError,
+    SSHTunnelDatabasePortError,
     SSHTunnelingNotEnabledError,
     SSHTunnelInvalidError,
 )
@@ -103,6 +105,7 @@ class CreateDatabaseCommand(BaseCommand):
             SSHTunnelInvalidError,
             SSHTunnelCreateFailedError,
             SSHTunnelingNotEnabledError,
+            SSHTunnelDatabasePortError,
         ) as ex:
             db.session.rollback()
             event_logger.log_with_context(
@@ -140,6 +143,7 @@ class CreateDatabaseCommand(BaseCommand):
             # Check database_name uniqueness
             if not DatabaseDAO.validate_uniqueness(database_name):
                 exceptions.append(DatabaseExistsValidationError())
+
         if exceptions:
             exception = DatabaseInvalidError()
             exception.extend(exceptions)
diff --git a/superset/commands/database/ssh_tunnel/create.py b/superset/commands/database/ssh_tunnel/create.py
index 59e083d4d8..287accc5aa 100644
--- a/superset/commands/database/ssh_tunnel/create.py
+++ b/superset/commands/database/ssh_tunnel/create.py
@@ -23,11 +23,13 @@ from marshmallow import ValidationError
 from superset.commands.base import BaseCommand
 from superset.commands.database.ssh_tunnel.exceptions import (
     SSHTunnelCreateFailedError,
+    SSHTunnelDatabasePortError,
     SSHTunnelInvalidError,
     SSHTunnelRequiredFieldValidationError,
 )
 from superset.daos.database import SSHTunnelDAO
 from superset.daos.exceptions import DAOCreateFailedError
+from superset.databases.utils import make_url_safe
 from superset.extensions import event_logger
 from superset.models.core import Database
 
@@ -35,9 +37,12 @@ logger = logging.getLogger(__name__)
 
 
 class CreateSSHTunnelCommand(BaseCommand):
+    _database: Database
+
     def __init__(self, database: Database, data: dict[str, Any]):
         self._properties = data.copy()
         self._properties["database"] = database
+        self._database = database
 
     def run(self) -> Model:
         try:
@@ -62,6 +67,9 @@ class CreateSSHTunnelCommand(BaseCommand):
         private_key_password: Optional[str] = self._properties.get(
             "private_key_password"
         )
+        url = make_url_safe(self._database.sqlalchemy_uri)
+        if not url.port:
+            raise SSHTunnelDatabasePortError()
         if not server_address:
             exceptions.append(SSHTunnelRequiredFieldValidationError("server_address"))
         if not server_port:
diff --git a/superset/commands/database/ssh_tunnel/exceptions.py b/superset/commands/database/ssh_tunnel/exceptions.py
index 0e3f91cae6..a0def8c087 100644
--- a/superset/commands/database/ssh_tunnel/exceptions.py
+++ b/superset/commands/database/ssh_tunnel/exceptions.py
@@ -38,6 +38,10 @@ class SSHTunnelInvalidError(CommandInvalidError):
     message = _("SSH Tunnel parameters are invalid.")
 
 
+class SSHTunnelDatabasePortError(CommandInvalidError):
+    message = _("A database port is required when connecting via SSH Tunnel.")
+
+
 class SSHTunnelUpdateFailedError(UpdateFailedError):
     message = _("SSH Tunnel could not be updated.")
 
diff --git a/superset/commands/database/ssh_tunnel/update.py b/superset/commands/database/ssh_tunnel/update.py
index 47f7d4947a..077ed4c321 100644
--- a/superset/commands/database/ssh_tunnel/update.py
+++ b/superset/commands/database/ssh_tunnel/update.py
@@ -21,6 +21,7 @@ from flask_appbuilder.models.sqla import Model
 
 from superset.commands.base import BaseCommand
 from superset.commands.database.ssh_tunnel.exceptions import (
+    SSHTunnelDatabasePortError,
     SSHTunnelInvalidError,
     SSHTunnelNotFoundError,
     SSHTunnelRequiredFieldValidationError,
@@ -29,6 +30,7 @@ from superset.commands.database.ssh_tunnel.exceptions import (
 from superset.daos.database import SSHTunnelDAO
 from superset.daos.exceptions import DAOUpdateFailedError
 from superset.databases.ssh_tunnel.models import SSHTunnel
+from superset.databases.utils import make_url_safe
 
 logger = logging.getLogger(__name__)
 
@@ -62,6 +64,8 @@ class UpdateSSHTunnelCommand(BaseCommand):
         self._model = SSHTunnelDAO.find_by_id(self._model_id)
         if not self._model:
             raise SSHTunnelNotFoundError()
+
+        url = make_url_safe(self._model.database.sqlalchemy_uri)
         private_key: Optional[str] = self._properties.get("private_key")
         private_key_password: Optional[str] = self._properties.get(
             "private_key_password"
@@ -70,3 +74,5 @@ class UpdateSSHTunnelCommand(BaseCommand):
             raise SSHTunnelInvalidError(
                 exceptions=[SSHTunnelRequiredFieldValidationError("private_key")]
             )
+        if not url.port:
+            raise SSHTunnelDatabasePortError()
diff --git a/superset/commands/database/test_connection.py b/superset/commands/database/test_connection.py
index 0ffdf3ddd9..e91eec3a89 100644
--- a/superset/commands/database/test_connection.py
+++ b/superset/commands/database/test_connection.py
@@ -32,8 +32,11 @@ from superset.commands.database.exceptions import (
     DatabaseTestConnectionDriverError,
     DatabaseTestConnectionUnexpectedError,
 )
-from superset.commands.database.ssh_tunnel.exceptions import SSHTunnelingNotEnabledError
-from superset.daos.database import DatabaseDAO, SSHTunnelDAO
+from superset.commands.database.ssh_tunnel.exceptions import (
+    SSHTunnelDatabasePortError,
+    SSHTunnelingNotEnabledError,
+)
+from superset.daos.database import DatabaseDAO
 from superset.databases.ssh_tunnel.models import SSHTunnel
 from superset.databases.utils import make_url_safe
 from superset.errors import ErrorLevel, SupersetErrorType
@@ -44,7 +47,6 @@ from superset.exceptions import (
 )
 from superset.extensions import event_logger
 from superset.models.core import Database
-from superset.utils.ssh_tunnel import unmask_password_info
 
 logger = logging.getLogger(__name__)
 
@@ -61,20 +63,22 @@ def get_log_connection_action(
 
 
 class TestConnectionDatabaseCommand(BaseCommand):
+    _model: Optional[Database] = None
+    _context: dict[str, Any]
+    _uri: str
+
     def __init__(self, data: dict[str, Any]):
         self._properties = data.copy()
-        self._model: Optional[Database] = None
 
-    def run(self) -> None:  # pylint: disable=too-many-statements, too-many-branches
-        self.validate()
-        ex_str = ""
+        if (database_name := self._properties.get("database_name")) is not None:
+            self._model = DatabaseDAO.get_database_by_name(database_name)
+
         uri = self._properties.get("sqlalchemy_uri", "")
         if self._model and uri == self._model.safe_sqlalchemy_uri():
             uri = self._model.sqlalchemy_uri_decrypted
-        ssh_tunnel = self._properties.get("ssh_tunnel")
 
-        # context for error messages
         url = make_url_safe(uri)
+
         context = {
             "hostname": url.host,
             "password": url.password,
@@ -83,6 +87,14 @@ class TestConnectionDatabaseCommand(BaseCommand):
             "database": url.database,
         }
 
+        self._context = context
+        self._uri = uri
+
+    def run(self) -> None:  # pylint: disable=too-many-statements, too-many-branches
+        self.validate()
+        ex_str = ""
+        ssh_tunnel = self._properties.get("ssh_tunnel")
+
         serialized_encrypted_extra = self._properties.get(
             "masked_encrypted_extra",
             "{}",
@@ -103,20 +115,11 @@ class TestConnectionDatabaseCommand(BaseCommand):
                 encrypted_extra=serialized_encrypted_extra,
             )
 
-            database.set_sqlalchemy_uri(uri)
+            database.set_sqlalchemy_uri(self._uri)
             database.db_engine_spec.mutate_db_for_connection_test(database)
 
             # Generate tunnel if present in the properties
             if ssh_tunnel:
-                if not is_feature_enabled("SSH_TUNNELING"):
-                    raise SSHTunnelingNotEnabledError()
-                # If there's an existing tunnel for that DB we need to use the stored
-                # password, private_key and private_key_password instead
-                if ssh_tunnel_id := ssh_tunnel.pop("id", None):
-                    if existing_ssh_tunnel := SSHTunnelDAO.find_by_id(ssh_tunnel_id):
-                        ssh_tunnel = unmask_password_info(
-                            ssh_tunnel, existing_ssh_tunnel
-                        )
                 ssh_tunnel = SSHTunnel(**ssh_tunnel)
 
             event_logger.log_with_context(
@@ -186,7 +189,7 @@ class TestConnectionDatabaseCommand(BaseCommand):
                 engine=database.db_engine_spec.__name__,
             )
             # check for custom errors (wrong username, wrong password, etc)
-            errors = database.db_engine_spec.extract_errors(ex, context)
+            errors = database.db_engine_spec.extract_errors(ex, self._context)
             raise SupersetErrorsException(errors) from ex
         except SupersetSecurityException as ex:
             event_logger.log_with_context(
@@ -221,9 +224,12 @@ class TestConnectionDatabaseCommand(BaseCommand):
                 ),
                 engine=database.db_engine_spec.__name__,
             )
-            errors = database.db_engine_spec.extract_errors(ex, context)
+            errors = database.db_engine_spec.extract_errors(ex, self._context)
             raise DatabaseTestConnectionUnexpectedError(errors) from ex
 
     def validate(self) -> None:
-        if (database_name := self._properties.get("database_name")) is not None:
-            self._model = DatabaseDAO.get_database_by_name(database_name)
+        if self._properties.get("ssh_tunnel"):
+            if not is_feature_enabled("SSH_TUNNELING"):
+                raise SSHTunnelingNotEnabledError()
+            if not self._context.get("port"):
+                raise SSHTunnelDatabasePortError()
diff --git a/superset/commands/database/update.py b/superset/commands/database/update.py
index b891c8f157..88539a2c7b 100644
--- a/superset/commands/database/update.py
+++ b/superset/commands/database/update.py
@@ -18,6 +18,7 @@ import logging
 from typing import Any, Optional
 
 from flask_appbuilder.models.sqla import Model
+from flask_babel import gettext as _
 from marshmallow import ValidationError
 
 from superset import is_feature_enabled
@@ -33,6 +34,7 @@ from superset.commands.database.ssh_tunnel.create import CreateSSHTunnelCommand
 from superset.commands.database.ssh_tunnel.delete import DeleteSSHTunnelCommand
 from superset.commands.database.ssh_tunnel.exceptions import (
     SSHTunnelCreateFailedError,
+    SSHTunnelDatabasePortError,
     SSHTunnelDeleteFailedError,
     SSHTunnelingNotEnabledError,
     SSHTunnelInvalidError,
@@ -49,15 +51,19 @@ logger = logging.getLogger(__name__)
 
 
 class UpdateDatabaseCommand(BaseCommand):
+    _model: Optional[Database]
+
     def __init__(self, model_id: int, data: dict[str, Any]):
         self._properties = data.copy()
         self._model_id = model_id
-        self._model: Optional[Database] = None
+        self._model = DatabaseDAO.find_by_id(self._model_id)
 
     def run(self) -> Model:
-        self.validate()
         if not self._model:
             raise DatabaseNotFoundError()
+
+        self.validate()
+
         old_database_name = self._model.database_name
 
         # unmask ``encrypted_extra``
@@ -72,32 +78,34 @@ class UpdateDatabaseCommand(BaseCommand):
             database = DatabaseDAO.update(self._model, self._properties, commit=False)
             database.set_sqlalchemy_uri(database.sqlalchemy_uri)
 
-            existing_ssh_tunnel_model = DatabaseDAO.get_ssh_tunnel(database.id)
+            ssh_tunnel = DatabaseDAO.get_ssh_tunnel(database.id)
 
             if "ssh_tunnel" in self._properties:
                 if not is_feature_enabled("SSH_TUNNELING"):
                     db.session.rollback()
                     raise SSHTunnelingNotEnabledError()
 
-                if not self._properties.get("ssh_tunnel") and existing_ssh_tunnel_model:
+                if not self._properties.get("ssh_tunnel") and ssh_tunnel:
                     # We need to remove the existing tunnel
                     try:
-                        DeleteSSHTunnelCommand(existing_ssh_tunnel_model.id).run()
+                        DeleteSSHTunnelCommand(ssh_tunnel.id).run()
+                        ssh_tunnel = None
                     except SSHTunnelDeleteFailedError as ex:
                         raise ex
                     except Exception as ex:
                         raise DatabaseUpdateFailedError() from ex
 
                 if ssh_tunnel_properties := self._properties.get("ssh_tunnel"):
-                    if existing_ssh_tunnel_model is None:
+                    if ssh_tunnel is None:
                         # We couldn't found an existing tunnel so we need to create one
                         try:
-                            CreateSSHTunnelCommand(
+                            ssh_tunnel = CreateSSHTunnelCommand(
                                 database, ssh_tunnel_properties
                             ).run()
                         except (
                             SSHTunnelInvalidError,
                             SSHTunnelCreateFailedError,
+                            SSHTunnelDatabasePortError,
                         ) as ex:
                             # So we can show the original message
                             raise ex
@@ -106,12 +114,14 @@ class UpdateDatabaseCommand(BaseCommand):
                     else:
                         # We found an existing tunnel so we need to update it
                         try:
-                            UpdateSSHTunnelCommand(
-                                existing_ssh_tunnel_model.id, ssh_tunnel_properties
+                            ssh_tunnel_id = ssh_tunnel.id
+                            ssh_tunnel = UpdateSSHTunnelCommand(
+                                ssh_tunnel_id, ssh_tunnel_properties
                             ).run()
                         except (
                             SSHTunnelInvalidError,
                             SSHTunnelUpdateFailedError,
+                            SSHTunnelDatabasePortError,
                         ) as ex:
                             # So we can show the original message
                             raise ex
@@ -121,7 +131,6 @@ class UpdateDatabaseCommand(BaseCommand):
             # adding a new database we always want to force refresh schema list
             # TODO Improve this simplistic implementation for catching DB conn fails
             try:
-                ssh_tunnel = DatabaseDAO.get_ssh_tunnel(database.id)
                 schemas = database.get_all_schema_names(ssh_tunnel=ssh_tunnel)
             except Exception as ex:
                 db.session.rollback()
@@ -189,10 +198,6 @@ class UpdateDatabaseCommand(BaseCommand):
 
     def validate(self) -> None:
         exceptions: list[ValidationError] = []
-        # Validate/populate model exists
-        self._model = DatabaseDAO.find_by_id(self._model_id)
-        if not self._model:
-            raise DatabaseNotFoundError()
         database_name: Optional[str] = self._properties.get("database_name")
         if database_name:
             # Check database_name uniqueness
diff --git a/superset/databases/api.py b/superset/databases/api.py
index 2f95bd0442..e6aca61a20 100644
--- a/superset/databases/api.py
+++ b/superset/databases/api.py
@@ -47,6 +47,7 @@ from superset.commands.database.export import ExportDatabasesCommand
 from superset.commands.database.importers.dispatcher import ImportDatabasesCommand
 from superset.commands.database.ssh_tunnel.delete import DeleteSSHTunnelCommand
 from superset.commands.database.ssh_tunnel.exceptions import (
+    SSHTunnelDatabasePortError,
     SSHTunnelDeleteFailedError,
     SSHTunnelingNotEnabledError,
 )
@@ -415,7 +416,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
                 exc_info=True,
             )
             return self.response_422(message=str(ex))
-        except SSHTunnelingNotEnabledError as ex:
+        except (SSHTunnelingNotEnabledError, SSHTunnelDatabasePortError) as ex:
             return self.response_400(message=str(ex))
         except SupersetException as ex:
             return self.response(ex.status, message=ex.message)
@@ -500,7 +501,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
                 exc_info=True,
             )
             return self.response_422(message=str(ex))
-        except SSHTunnelingNotEnabledError as ex:
+        except (SSHTunnelingNotEnabledError, SSHTunnelDatabasePortError) as ex:
             return self.response_400(message=str(ex))
 
     @expose("/<int:pk>", methods=("DELETE",))


(superset) 02/10: Lint

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 0f73196d7ebabc53717d730be57f8cd26d0db0ee
Author: geido <di...@gmail.com>
AuthorDate: Fri Feb 16 16:16:55 2024 +0200

    Lint
---
 superset-frontend/src/features/databases/DatabaseModal/index.tsx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
index 46b090f465..6957e21af3 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
@@ -23,7 +23,6 @@ import {
   FeatureFlag,
   isFeatureEnabled,
   getExtensionsRegistry,
-  SupersetClient,
 } from '@superset-ui/core';
 import React, {
   FunctionComponent,


(superset) 08/10: Clean up

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit beba29db729b7b3ecaa30e8e15133a9239a38d52
Author: geido <di...@gmail.com>
AuthorDate: Tue Feb 20 16:25:31 2024 +0200

    Clean up
---
 .../src/features/databases/DatabaseModal/index.tsx | 23 +++++++++-------------
 superset-frontend/src/views/CRUD/hooks.ts          |  2 +-
 superset/databases/api.py                          |  2 +-
 3 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
index 3607e5e400..eab71d02da 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
@@ -756,6 +756,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
 
   const onSave = async () => {
     let dbConfigExtraExtensionOnSaveError;
+
+    setLoading(true);
+
     dbConfigExtraExtension
       ?.onSave(extraExtensionComponentState, db)
       .then(({ error }: { error: any }) => {
@@ -764,6 +767,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
           addDangerToast(error);
         }
       });
+
     if (dbConfigExtraExtensionOnSaveError) {
       setLoading(false);
       return;
@@ -783,17 +787,10 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
         });
       }
 
-      // only do validation for non ssh tunnel connections
-      if (!dbToUpdate?.parameters?.ssh) {
-        // make sure that button spinner animates
-        setLoading(true);
-        const errors = await getValidation(dbToUpdate, true);
-        if ((validationErrors && !isEmpty(validationErrors)) || errors) {
-          setLoading(false);
-          return;
-        }
-        // end spinner animation
+      const errors = await getValidation(dbToUpdate, true);
+      if ((validationErrors && !isEmpty(validationErrors)) || errors) {
         setLoading(false);
+        return;
       }
 
       const parameters_schema = isEditMode
@@ -850,8 +847,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
       });
     }
 
-    setLoading(true);
-
     // strictly checking for false as an indication that the toggle got unchecked
     if (isSSHTunneling && useSSHTunneling === false) {
       // remove ssh tunnel
@@ -1542,8 +1537,8 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
         typeof dbErrors === 'object'
           ? Object.values(dbErrors)
           : typeof dbErrors === 'string'
-          ? [dbErrors]
-          : [];
+            ? [dbErrors]
+            : [];
     } else if (
       !isEmpty(validationErrors) &&
       validationErrors?.error_type === 'GENERIC_DB_ENGINE_ERROR'
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 85f7c60252..bd6607d31b 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -745,7 +745,7 @@ export function useDatabaseValidation() {
   const getValidation = useCallback(
     (database: Partial<DatabaseObject> | null, onCreate = false) => {
       if (database?.parameters?.ssh) {
-        // when ssh tunnel is enabled we don't want to render any validation errors
+        // TODO: /validate_parameters/ and related utils should support ssh tunnel
         setValidationErrors(null);
         return [];
       }
diff --git a/superset/databases/api.py b/superset/databases/api.py
index e6aca61a20..4d7d4c531a 100644
--- a/superset/databases/api.py
+++ b/superset/databases/api.py
@@ -919,7 +919,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
         try:
             TestConnectionDatabaseCommand(item).run()
             return self.response(200, message="OK")
-        except SSHTunnelingNotEnabledError as ex:
+        except (SSHTunnelingNotEnabledError, SSHTunnelDatabasePortError) as ex:
             return self.response_400(message=str(ex))
 
     @expose("/<int:pk>/related_objects/", methods=("GET",))


(superset) 07/10: Raise SSHTunnelDatabasePortError

Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 7bc2283027fd36c7b199c3d963b04af6f174935f
Author: geido <di...@gmail.com>
AuthorDate: Tue Feb 20 15:07:52 2024 +0200

    Raise SSHTunnelDatabasePortError
---
 superset/commands/database/create.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/superset/commands/database/create.py b/superset/commands/database/create.py
index 1ddc08e6a1..9efb39b75a 100644
--- a/superset/commands/database/create.py
+++ b/superset/commands/database/create.py
@@ -59,7 +59,11 @@ class CreateDatabaseCommand(BaseCommand):
         try:
             # Test connection before starting create transaction
             TestConnectionDatabaseCommand(self._properties).run()
-        except (SupersetErrorsException, SSHTunnelingNotEnabledError) as ex:
+        except (
+            SupersetErrorsException,
+            SSHTunnelingNotEnabledError,
+            SSHTunnelDatabasePortError,
+        ) as ex:
             event_logger.log_with_context(
                 action=f"db_creation_failed.{ex.__class__.__name__}",
                 engine=self._properties.get("sqlalchemy_uri", "").split(":")[0],