mirror of
https://github.com/apache/superset.git
synced 2026-07-04 22:05:32 +00:00
Compare commits
8 Commits
fix/saniti
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e152d9bb7 | ||
|
|
004c401c97 | ||
|
|
a9aabdaedf | ||
|
|
210389478b | ||
|
|
43f2816240 | ||
|
|
c3fe0a40eb | ||
|
|
71ac6c64e2 | ||
|
|
81a437826f |
@@ -64,7 +64,7 @@ dependencies = [
|
||||
"flask-wtf>=1.3.0, <2.0",
|
||||
"geopy",
|
||||
"greenlet<=3.5.1, >=3.5.1",
|
||||
"gunicorn>=25.3.0, <26; sys_platform != 'win32'",
|
||||
"gunicorn>=26.0.0, <27; sys_platform != 'win32'",
|
||||
"hashids>=1.3.1, <2",
|
||||
# holidays>=0.45 required for security fix
|
||||
"holidays>=0.45, <1",
|
||||
@@ -101,7 +101,7 @@ dependencies = [
|
||||
"pyyaml>=6.0.3, <7.0.0",
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=5.0.0, <6.0",
|
||||
"rison>=2.0.0, <3.0",
|
||||
"rison>=2.0.1, <3.0",
|
||||
"selenium>=4.45.0, <5.0",
|
||||
"shillelagh[gsheetsapi]>=1.4.4, <2.0",
|
||||
"sshtunnel>=0.4.0, <0.5",
|
||||
@@ -174,11 +174,11 @@ hive = [
|
||||
]
|
||||
impala = ["impyla>0.16.2, <0.23"]
|
||||
kusto = ["sqlalchemy-kusto>=3.1.2, <4"]
|
||||
kylin = ["kylinpy>=2.8.1, <2.9"]
|
||||
kylin = ["kylinpy>=2.8.4, <2.9"]
|
||||
mssql = ["pymssql>=2.3.13, <3"]
|
||||
# motherduck is an alias for duckdb - MotherDuck works via the duckdb driver
|
||||
motherduck = ["apache-superset[duckdb]"]
|
||||
mysql = ["mysqlclient>=2.1.0, <3"]
|
||||
mysql = ["mysqlclient>=2.2.8, <3"]
|
||||
ocient = [
|
||||
"sqlalchemy-ocient>=1.0.0",
|
||||
"pyocient>=1.0.15, <4",
|
||||
@@ -216,7 +216,7 @@ netezza = ["nzalchemy>=11.0.2, < 11.2"]
|
||||
starrocks = ["starrocks>=1.3.3, <2"]
|
||||
doris = ["pydoris>=1.0.0, <2.0.0"]
|
||||
oceanbase = ["oceanbase_py>=0.0.1.2"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.22", "ydb-sqlglot-plugin>=0.2.5"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.22", "ydb-sqlglot-plugin>=0.2.8"]
|
||||
development = [
|
||||
# no bounds for apache-superset-extensions-cli until a stable version
|
||||
"apache-superset-extensions-cli",
|
||||
|
||||
@@ -169,7 +169,7 @@ greenlet==3.5.1
|
||||
# apache-superset (pyproject.toml)
|
||||
# shillelagh
|
||||
# sqlalchemy
|
||||
gunicorn==25.3.0
|
||||
gunicorn==26.0.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
h11==0.16.0
|
||||
# via wsproto
|
||||
@@ -369,7 +369,7 @@ rfc3339-validator==0.1.4
|
||||
# via openapi-schema-validator
|
||||
rich==13.9.4
|
||||
# via flask-limiter
|
||||
rison==2.0.0
|
||||
rison==2.0.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
rpds-py==0.25.0
|
||||
# via
|
||||
|
||||
@@ -393,7 +393,7 @@ grpcio==1.81.1
|
||||
# grpcio-status
|
||||
grpcio-status==1.60.1
|
||||
# via google-api-core
|
||||
gunicorn==25.3.0
|
||||
gunicorn==26.0.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -572,7 +572,7 @@ msgspec==0.19.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-session
|
||||
mysqlclient==2.2.6
|
||||
mysqlclient==2.2.8
|
||||
# via apache-superset
|
||||
nh3==0.3.5
|
||||
# via
|
||||
@@ -913,7 +913,7 @@ rich==13.9.4
|
||||
# rich-rst
|
||||
rich-rst==1.3.1
|
||||
# via cyclopts
|
||||
rison==2.0.0
|
||||
rison==2.0.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
|
||||
2
superset-frontend/package-lock.json
generated
2
superset-frontend/package-lock.json
generated
@@ -45112,7 +45112,7 @@
|
||||
"@deck.gl/extensions": "~9.2.9",
|
||||
"@deck.gl/geo-layers": "~9.2.5",
|
||||
"@deck.gl/layers": "~9.2.5",
|
||||
"@deck.gl/mapbox": "^9.3.5",
|
||||
"@deck.gl/mapbox": "~9.3.5",
|
||||
"@deck.gl/mesh-layers": "~9.2.5",
|
||||
"@luma.gl/constants": "~9.2.5",
|
||||
"@luma.gl/core": "~9.2.5",
|
||||
|
||||
@@ -355,7 +355,7 @@ export const hydrateDashboard =
|
||||
'Superset',
|
||||
roles,
|
||||
),
|
||||
superset_can_csv: findPermission('can_csv', 'Superset', roles),
|
||||
superset_can_download: findPermission('can_csv', 'Superset', roles),
|
||||
common: {
|
||||
// legacy, please use state.common instead
|
||||
conf: common?.conf,
|
||||
|
||||
@@ -35,7 +35,7 @@ jest.mock('src/dashboard/components/SliceHeaderControls', () => ({
|
||||
data-cached-dttm={props.cachedDttm}
|
||||
data-updated-dttm={props.updatedDttm}
|
||||
data-superset-can-explore={props.supersetCanExplore}
|
||||
data-superset-can-csv={props.supersetCanCSV}
|
||||
data-superset-can-download={props.supersetCanDownload}
|
||||
data-component-id={props.componentId}
|
||||
data-dashboard-id={props.dashboardId}
|
||||
data-is-full-size={props.isFullSize}
|
||||
@@ -144,7 +144,7 @@ const createProps = (overrides: any = {}) => ({
|
||||
isExpanded: false,
|
||||
sliceName: 'Vaccine Candidates per Phase',
|
||||
supersetCanExplore: true,
|
||||
supersetCanCSV: true,
|
||||
supersetCanDownload: true,
|
||||
slice: {
|
||||
slice_id: MOCKED_CHART_ID,
|
||||
slice_url: `/explore/?form_data=%7B%22slice_id%22%3A%20${MOCKED_CHART_ID}%7D`,
|
||||
@@ -222,7 +222,7 @@ test('Should render - default props', () => {
|
||||
delete props.isExpanded;
|
||||
delete props.sliceName;
|
||||
delete props.supersetCanExplore;
|
||||
delete props.supersetCanCSV;
|
||||
delete props.supersetCanDownload;
|
||||
|
||||
render(<SliceHeader {...props} />, {
|
||||
useRedux: true,
|
||||
@@ -250,7 +250,7 @@ test('Should render default props and "call" actions', () => {
|
||||
delete props.isExpanded;
|
||||
delete props.sliceName;
|
||||
delete props.supersetCanExplore;
|
||||
delete props.supersetCanCSV;
|
||||
delete props.supersetCanDownload;
|
||||
|
||||
render(<SliceHeader {...props} />, {
|
||||
useRedux: true,
|
||||
@@ -459,7 +459,7 @@ test('Correct props to "SliceHeaderControls"', () => {
|
||||
'false',
|
||||
);
|
||||
expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
|
||||
'data-superset-can-csv',
|
||||
'data-superset-can-download',
|
||||
'true',
|
||||
);
|
||||
expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
|
||||
|
||||
@@ -157,7 +157,7 @@ const SliceHeader = forwardRef<HTMLDivElement, SliceHeaderProps>(
|
||||
sliceName = '',
|
||||
supersetCanExplore = false,
|
||||
supersetCanShare = false,
|
||||
supersetCanCSV = false,
|
||||
supersetCanDownload = false,
|
||||
exportPivotCSV,
|
||||
exportFullCSV,
|
||||
exportFullXLSX,
|
||||
@@ -367,7 +367,7 @@ const SliceHeader = forwardRef<HTMLDivElement, SliceHeaderProps>(
|
||||
exportFullXLSX={exportFullXLSX}
|
||||
supersetCanExplore={supersetCanExplore}
|
||||
supersetCanShare={supersetCanShare}
|
||||
supersetCanCSV={supersetCanCSV}
|
||||
supersetCanDownload={supersetCanDownload}
|
||||
componentId={componentId}
|
||||
dashboardId={dashboardId}
|
||||
addSuccessToast={addSuccessToast}
|
||||
|
||||
@@ -98,7 +98,7 @@ const buildProps = (): SliceHeaderControlsProps =>
|
||||
cachedDttm: [''],
|
||||
updatedDttm: 0,
|
||||
supersetCanExplore: true,
|
||||
supersetCanCSV: true,
|
||||
supersetCanDownload: true,
|
||||
componentId: 'CHART-subdir',
|
||||
dashboardId: 26,
|
||||
isFullSize: false,
|
||||
|
||||
@@ -87,7 +87,7 @@ const createProps = (viz_type = VizType.Sunburst) =>
|
||||
cachedDttm: [''],
|
||||
updatedDttm: 1617213803803,
|
||||
supersetCanExplore: true,
|
||||
supersetCanCSV: true,
|
||||
supersetCanDownload: true,
|
||||
componentId: 'CHART-fYo7IyvKZQ',
|
||||
dashboardId: 26,
|
||||
isFullSize: false,
|
||||
|
||||
@@ -141,7 +141,7 @@ export interface SliceHeaderControlsProps {
|
||||
|
||||
supersetCanExplore?: boolean;
|
||||
supersetCanShare?: boolean;
|
||||
supersetCanCSV?: boolean;
|
||||
supersetCanDownload?: boolean;
|
||||
|
||||
crossFiltersEnabled?: boolean;
|
||||
}
|
||||
@@ -519,7 +519,7 @@ const SliceHeaderControls = (
|
||||
dataSize={20}
|
||||
isRequest
|
||||
isVisible
|
||||
canDownload={!!props.supersetCanCSV}
|
||||
canDownload={!!props.supersetCanDownload}
|
||||
columnDisplayNames={datasetWithVerboseMap?.verbose_map}
|
||||
/>
|
||||
}
|
||||
@@ -562,7 +562,7 @@ const SliceHeaderControls = (
|
||||
newMenuItems.push(shareMenuItems);
|
||||
}
|
||||
|
||||
if (props.supersetCanCSV) {
|
||||
if (props.supersetCanDownload) {
|
||||
newMenuItems.push({
|
||||
type: 'submenu',
|
||||
key: MenuKeys.Download,
|
||||
@@ -593,7 +593,7 @@ const SliceHeaderControls = (
|
||||
icon: <Icons.FileOutlined css={dropdownIconsStyles} />,
|
||||
},
|
||||
...(isFeatureEnabled(FeatureFlag.AllowFullCsvExport) &&
|
||||
props.supersetCanCSV &&
|
||||
props.supersetCanDownload &&
|
||||
isTable
|
||||
? [
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ export interface SliceHeaderControlsProps {
|
||||
|
||||
supersetCanExplore?: boolean;
|
||||
supersetCanShare?: boolean;
|
||||
supersetCanCSV?: boolean;
|
||||
supersetCanDownload?: boolean;
|
||||
|
||||
crossFiltersEnabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ const defaultState = {
|
||||
id: props.dashboardId,
|
||||
superset_can_explore: false,
|
||||
superset_can_share: false,
|
||||
superset_can_csv: false,
|
||||
superset_can_download: false,
|
||||
common: { conf: { SUPERSET_WEBSERVER_TIMEOUT: 0, SQL_MAX_ROW: 666 } },
|
||||
},
|
||||
dashboardLayout: {
|
||||
@@ -181,7 +181,10 @@ test('should call exportChart when exportCSV is clicked', async () => {
|
||||
const { findByText, getByRole } = setup(
|
||||
{},
|
||||
{
|
||||
dashboardInfo: { ...defaultState.dashboardInfo, superset_can_csv: true },
|
||||
dashboardInfo: {
|
||||
...defaultState.dashboardInfo,
|
||||
superset_can_download: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
@@ -211,7 +214,10 @@ test('should call exportChart with row_limit props.maxRows when exportFullCSV is
|
||||
const { findByText, getByRole } = setup(
|
||||
{},
|
||||
{
|
||||
dashboardInfo: { ...defaultState.dashboardInfo, superset_can_csv: true },
|
||||
dashboardInfo: {
|
||||
...defaultState.dashboardInfo,
|
||||
superset_can_download: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
@@ -239,7 +245,10 @@ test('should call exportChart when exportXLSX is clicked', async () => {
|
||||
const { findByText, getByRole } = setup(
|
||||
{},
|
||||
{
|
||||
dashboardInfo: { ...defaultState.dashboardInfo, superset_can_csv: true },
|
||||
dashboardInfo: {
|
||||
...defaultState.dashboardInfo,
|
||||
superset_can_download: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
@@ -266,7 +275,10 @@ test('should call exportChart with row_limit props.maxRows when exportFullXLSX i
|
||||
const { findByText, getByRole } = setup(
|
||||
{},
|
||||
{
|
||||
dashboardInfo: { ...defaultState.dashboardInfo, superset_can_csv: true },
|
||||
dashboardInfo: {
|
||||
...defaultState.dashboardInfo,
|
||||
superset_can_download: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
|
||||
@@ -218,9 +218,9 @@ const Chart = (props: ChartProps) => {
|
||||
(state: RootState) =>
|
||||
!!(state.dashboardInfo as JsonObject).superset_can_share,
|
||||
);
|
||||
const supersetCanCSV = useSelector(
|
||||
const supersetCanDownload = useSelector(
|
||||
(state: RootState) =>
|
||||
!!(state.dashboardInfo as JsonObject).superset_can_csv,
|
||||
!!(state.dashboardInfo as JsonObject).superset_can_download,
|
||||
);
|
||||
const timeout: number = useSelector(
|
||||
(state: RootState) =>
|
||||
@@ -710,7 +710,7 @@ const Chart = (props: ChartProps) => {
|
||||
sliceName={props.sliceName}
|
||||
supersetCanExplore={supersetCanExplore}
|
||||
supersetCanShare={supersetCanShare}
|
||||
supersetCanCSV={supersetCanCSV}
|
||||
supersetCanDownload={supersetCanDownload}
|
||||
componentId={props.componentId}
|
||||
dashboardId={props.dashboardId}
|
||||
filters={getActiveFilters() || EMPTY_OBJECT}
|
||||
|
||||
@@ -181,6 +181,7 @@ const NAME_REQUIRED_REGEX = /^name is required$/i;
|
||||
const COLUMN_REQUIRED_REGEX = /^column is required$/i;
|
||||
const PRE_FILTER_REQUIRED_REGEX = /^pre-filter is required$/i;
|
||||
const DEFAULT_VALUE_INVALID_REGEX = /choose.*valid value/i;
|
||||
const REMOVE_FILTER_BUTTON_REGEX = /Remove filter/i;
|
||||
|
||||
const props: FiltersConfigModalProps = {
|
||||
isOpen: true,
|
||||
@@ -974,13 +975,15 @@ test('restores a deleted filter via the "Restore filter" button', async () => {
|
||||
|
||||
const filterContainer = screen.getByTestId('filter-title-container');
|
||||
const firstTab = within(filterContainer).getAllByRole('tab')[0];
|
||||
fireEvent.click(within(firstTab).getByRole('button', { name: /delete/i }));
|
||||
fireEvent.click(
|
||||
within(firstTab).getByRole('button', { name: REMOVE_FILTER_BUTTON_REGEX }),
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByText(/you have removed this filter/i),
|
||||
).toBeInTheDocument();
|
||||
const restoreButton = screen.getByTestId('restore-filter-button');
|
||||
await userEvent.click(restoreButton);
|
||||
userEvent.click(restoreButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
@@ -1009,11 +1012,13 @@ test('undoes a filter deletion via the sidebar "Undo?" link', async () => {
|
||||
|
||||
const filterContainer = screen.getByTestId('filter-title-container');
|
||||
const firstTab = within(filterContainer).getAllByRole('tab')[0];
|
||||
fireEvent.click(within(firstTab).getByRole('button', { name: /delete/i }));
|
||||
fireEvent.click(
|
||||
within(firstTab).getByRole('button', { name: REMOVE_FILTER_BUTTON_REGEX }),
|
||||
);
|
||||
|
||||
const undoButton = await screen.findByTestId('undo-button');
|
||||
expect(undoButton).toHaveTextContent(/undo\?/i);
|
||||
await userEvent.click(undoButton);
|
||||
userEvent.click(undoButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
|
||||
@@ -246,7 +246,7 @@ class Superset(BaseSupersetView):
|
||||
@permission_name("explore_json")
|
||||
@expose("/explore_json/data/<cache_key>", methods=("GET",))
|
||||
@check_resource_permissions(check_explore_cache_perms)
|
||||
@deprecated(eol_version="5.0.0")
|
||||
@deprecated(eol_version="5.0.0", new_target="/api/v1/chart/data/<cache_key>")
|
||||
def explore_json_data(self, cache_key: str) -> FlaskResponse:
|
||||
"""Serves cached result data for async explore_json calls
|
||||
|
||||
|
||||
Reference in New Issue
Block a user