mirror of
https://github.com/apache/superset.git
synced 2026-04-07 10:31:50 +00:00
fix: dashboard performance (#28609)
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com> Co-authored-by: Joe Li <joe@preset.io>
This commit is contained in:
committed by
GitHub
parent
f9d2451b23
commit
87110ebce4
@@ -45,7 +45,7 @@ dependencies = [
|
|||||||
"cryptography>=42.0.4, <43.0.0",
|
"cryptography>=42.0.4, <43.0.0",
|
||||||
"deprecation>=2.1.0, <2.2.0",
|
"deprecation>=2.1.0, <2.2.0",
|
||||||
"flask>=2.2.5, <3.0.0",
|
"flask>=2.2.5, <3.0.0",
|
||||||
"flask-appbuilder>=4.4.1, <5.0.0",
|
"flask-appbuilder>=4.5.0, <5.0.0",
|
||||||
"flask-caching>=2.1.0, <3",
|
"flask-caching>=2.1.0, <3",
|
||||||
"flask-compress>=1.13, <2.0",
|
"flask-compress>=1.13, <2.0",
|
||||||
"flask-talisman>=1.0.0, <2.0",
|
"flask-talisman>=1.0.0, <2.0",
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ apispec[yaml]==6.3.0
|
|||||||
# via flask-appbuilder
|
# via flask-appbuilder
|
||||||
apsw==3.46.0.0
|
apsw==3.46.0.0
|
||||||
# via shillelagh
|
# via shillelagh
|
||||||
async-timeout==4.0.3
|
|
||||||
# via redis
|
|
||||||
attrs==23.2.0
|
attrs==23.2.0
|
||||||
# via
|
# via
|
||||||
# cattrs
|
# cattrs
|
||||||
@@ -93,8 +91,6 @@ dnspython==2.6.1
|
|||||||
# via email-validator
|
# via email-validator
|
||||||
email-validator==2.1.1
|
email-validator==2.1.1
|
||||||
# via flask-appbuilder
|
# via flask-appbuilder
|
||||||
exceptiongroup==1.2.1
|
|
||||||
# via cattrs
|
|
||||||
flask==2.3.3
|
flask==2.3.3
|
||||||
# via
|
# via
|
||||||
# apache-superset
|
# apache-superset
|
||||||
@@ -109,7 +105,7 @@ flask==2.3.3
|
|||||||
# flask-session
|
# flask-session
|
||||||
# flask-sqlalchemy
|
# flask-sqlalchemy
|
||||||
# flask-wtf
|
# flask-wtf
|
||||||
flask-appbuilder==4.4.1
|
flask-appbuilder==4.5.0
|
||||||
# via apache-superset
|
# via apache-superset
|
||||||
flask-babel==2.0.0
|
flask-babel==2.0.0
|
||||||
# via flask-appbuilder
|
# via flask-appbuilder
|
||||||
@@ -360,7 +356,6 @@ typing-extensions==4.12.0
|
|||||||
# via
|
# via
|
||||||
# alembic
|
# alembic
|
||||||
# apache-superset
|
# apache-superset
|
||||||
# cattrs
|
|
||||||
# flask-limiter
|
# flask-limiter
|
||||||
# limits
|
# limits
|
||||||
# shillelagh
|
# shillelagh
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
# via
|
# via
|
||||||
# -r requirements/base.in
|
# -r requirements/base.in
|
||||||
# -r requirements/development.in
|
# -r requirements/development.in
|
||||||
|
appnope==0.1.4
|
||||||
|
# via ipython
|
||||||
astroid==3.1.0
|
astroid==3.1.0
|
||||||
# via pylint
|
# via pylint
|
||||||
boto3==1.34.112
|
boto3==1.34.112
|
||||||
@@ -175,7 +177,9 @@ protobuf==4.23.0
|
|||||||
psycopg2-binary==2.9.6
|
psycopg2-binary==2.9.6
|
||||||
# via apache-superset
|
# via apache-superset
|
||||||
pure-sasl==0.6.2
|
pure-sasl==0.6.2
|
||||||
# via thrift-sasl
|
# via
|
||||||
|
# pyhive
|
||||||
|
# thrift-sasl
|
||||||
pydata-google-auth==1.7.0
|
pydata-google-auth==1.7.0
|
||||||
# via pandas-gbq
|
# via pandas-gbq
|
||||||
pydruid==0.6.9
|
pydruid==0.6.9
|
||||||
@@ -184,7 +188,7 @@ pyee==11.0.1
|
|||||||
# via playwright
|
# via playwright
|
||||||
pyfakefs==5.3.5
|
pyfakefs==5.3.5
|
||||||
# via apache-superset
|
# via apache-superset
|
||||||
pyhive[presto]==0.7.0
|
pyhive[hive_pure_sasl]==0.7.0
|
||||||
# via apache-superset
|
# via apache-superset
|
||||||
pyinstrument==4.4.0
|
pyinstrument==4.4.0
|
||||||
# via apache-superset
|
# via apache-superset
|
||||||
@@ -228,10 +232,9 @@ tableschema==1.20.10
|
|||||||
thrift==0.16.0
|
thrift==0.16.0
|
||||||
# via
|
# via
|
||||||
# apache-superset
|
# apache-superset
|
||||||
|
# pyhive
|
||||||
# thrift-sasl
|
# thrift-sasl
|
||||||
thrift-sasl==0.4.3
|
thrift-sasl==0.4.3
|
||||||
# via apache-superset
|
|
||||||
tomli==2.0.1
|
|
||||||
# via
|
# via
|
||||||
# build
|
# build
|
||||||
# coverage
|
# coverage
|
||||||
|
|||||||
158
superset-frontend/src/features/dashboards/DashboardCard.test.tsx
Normal file
158
superset-frontend/src/features/dashboards/DashboardCard.test.tsx
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { FeatureFlag, SupersetClient } from '@superset-ui/core';
|
||||||
|
import * as uiCore from '@superset-ui/core';
|
||||||
|
|
||||||
|
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||||
|
|
||||||
|
import DashboardCard from './DashboardCard';
|
||||||
|
|
||||||
|
const mockDashboard = {
|
||||||
|
id: 1,
|
||||||
|
dashboard_title: 'Sample Dashboard',
|
||||||
|
certified_by: 'John Doe',
|
||||||
|
certification_details: 'Certified on 2022-01-01',
|
||||||
|
published: true,
|
||||||
|
url: '/dashboard/1',
|
||||||
|
thumbnail_url: '/thumbnails/1.png',
|
||||||
|
changed_on_delta_humanized: '2 days ago',
|
||||||
|
owners: [
|
||||||
|
{ id: 1, name: 'Alice', first_name: 'Alice', last_name: 'Doe' },
|
||||||
|
{ id: 2, name: 'Bob', first_name: 'Bob', last_name: 'Smith' },
|
||||||
|
],
|
||||||
|
changed_by_name: 'John Doe',
|
||||||
|
changed_by: 'john.doe@example.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockHasPerm = jest.fn().mockReturnValue(true);
|
||||||
|
const mockOpenDashboardEditModal = jest.fn();
|
||||||
|
const mockSaveFavoriteStatus = jest.fn();
|
||||||
|
const mockHandleBulkDashboardExport = jest.fn();
|
||||||
|
const mockOnDelete = jest.fn();
|
||||||
|
|
||||||
|
let isFeatureEnabledMock: jest.MockInstance<boolean, [feature: FeatureFlag]>;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
isFeatureEnabledMock = jest
|
||||||
|
.spyOn(uiCore, 'isFeatureEnabled')
|
||||||
|
.mockImplementation(() => true);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
isFeatureEnabledMock.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<DashboardCard
|
||||||
|
dashboard={mockDashboard}
|
||||||
|
hasPerm={mockHasPerm}
|
||||||
|
bulkSelectEnabled={false}
|
||||||
|
loading={false}
|
||||||
|
openDashboardEditModal={mockOpenDashboardEditModal}
|
||||||
|
saveFavoriteStatus={mockSaveFavoriteStatus}
|
||||||
|
favoriteStatus={false}
|
||||||
|
handleBulkDashboardExport={mockHandleBulkDashboardExport}
|
||||||
|
onDelete={mockOnDelete}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders the dashboard title', () => {
|
||||||
|
const titleElement = screen.getByText('Sample Dashboard');
|
||||||
|
expect(titleElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders the certification details', () => {
|
||||||
|
const certificationDetailsElement = screen.getByLabelText(/certified/i);
|
||||||
|
expect(certificationDetailsElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders the published status', () => {
|
||||||
|
const publishedElement = screen.getByText(/published/i);
|
||||||
|
expect(publishedElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders the modified date', () => {
|
||||||
|
const modifiedDateElement = screen.getByText('Modified 2 days ago');
|
||||||
|
expect(modifiedDateElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch thumbnail when dashboard has no thumbnail URL and feature flag is enabled', async () => {
|
||||||
|
const mockGet = jest.spyOn(SupersetClient, 'get').mockResolvedValue({
|
||||||
|
response: new Response(
|
||||||
|
JSON.stringify({ thumbnail_url: '/new-thumbnail.png' }),
|
||||||
|
),
|
||||||
|
json: () => Promise.resolve({ thumbnail_url: '/new-thumbnail.png' }),
|
||||||
|
});
|
||||||
|
const { rerender } = render(
|
||||||
|
<DashboardCard
|
||||||
|
dashboard={{
|
||||||
|
id: 1,
|
||||||
|
thumbnail_url: '',
|
||||||
|
changed_by_name: '',
|
||||||
|
changed_by: '',
|
||||||
|
dashboard_title: '',
|
||||||
|
published: false,
|
||||||
|
url: '',
|
||||||
|
owners: [],
|
||||||
|
}}
|
||||||
|
hasPerm={() => true}
|
||||||
|
bulkSelectEnabled={false}
|
||||||
|
loading={false}
|
||||||
|
saveFavoriteStatus={() => {}}
|
||||||
|
favoriteStatus={false}
|
||||||
|
handleBulkDashboardExport={() => {}}
|
||||||
|
onDelete={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockGet).toHaveBeenCalledWith({
|
||||||
|
endpoint: '/api/v1/dashboard/1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
rerender(
|
||||||
|
<DashboardCard
|
||||||
|
dashboard={{
|
||||||
|
id: 1,
|
||||||
|
thumbnail_url: '/new-thumbnail.png',
|
||||||
|
changed_by_name: '',
|
||||||
|
changed_by: '',
|
||||||
|
dashboard_title: '',
|
||||||
|
published: false,
|
||||||
|
url: '',
|
||||||
|
owners: [],
|
||||||
|
}}
|
||||||
|
hasPerm={() => true}
|
||||||
|
bulkSelectEnabled={false}
|
||||||
|
loading={false}
|
||||||
|
saveFavoriteStatus={() => {}}
|
||||||
|
favoriteStatus={false}
|
||||||
|
handleBulkDashboardExport={() => {}}
|
||||||
|
onDelete={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
mockGet.mockRestore();
|
||||||
|
});
|
||||||
@@ -16,9 +16,15 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import { isFeatureEnabled, FeatureFlag, t, useTheme } from '@superset-ui/core';
|
import {
|
||||||
|
isFeatureEnabled,
|
||||||
|
FeatureFlag,
|
||||||
|
t,
|
||||||
|
useTheme,
|
||||||
|
SupersetClient,
|
||||||
|
} from '@superset-ui/core';
|
||||||
import { CardStyles } from 'src/views/CRUD/utils';
|
import { CardStyles } from 'src/views/CRUD/utils';
|
||||||
import { AntdDropdown } from 'src/components';
|
import { AntdDropdown } from 'src/components';
|
||||||
import { Menu } from 'src/components/Menu';
|
import { Menu } from 'src/components/Menu';
|
||||||
@@ -62,6 +68,35 @@ function DashboardCard({
|
|||||||
const canExport = hasPerm('can_export');
|
const canExport = hasPerm('can_export');
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(null);
|
||||||
|
const [fetchingThumbnail, setFetchingThumbnail] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// fetch thumbnail only if it's not already fetched
|
||||||
|
if (
|
||||||
|
!fetchingThumbnail &&
|
||||||
|
dashboard.id &&
|
||||||
|
(thumbnailUrl === undefined || thumbnailUrl === null) &&
|
||||||
|
isFeatureEnabled(FeatureFlag.Thumbnails)
|
||||||
|
) {
|
||||||
|
// fetch thumbnail
|
||||||
|
if (dashboard.thumbnail_url) {
|
||||||
|
// set to empty string if null so that we don't
|
||||||
|
// keep fetching the thumbnail
|
||||||
|
setThumbnailUrl(dashboard.thumbnail_url || '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFetchingThumbnail(true);
|
||||||
|
SupersetClient.get({
|
||||||
|
endpoint: `/api/v1/dashboard/${dashboard.id}`,
|
||||||
|
}).then(({ json = {} }) => {
|
||||||
|
setThumbnailUrl(json.thumbnail_url || '');
|
||||||
|
setFetchingThumbnail(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [dashboard, thumbnailUrl]);
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
{canEdit && openDashboardEditModal && (
|
{canEdit && openDashboardEditModal && (
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ describe('DashboardList', () => {
|
|||||||
const callsD = fetchMock.calls(/dashboard\/\?q/);
|
const callsD = fetchMock.calls(/dashboard\/\?q/);
|
||||||
expect(callsD).toHaveLength(1);
|
expect(callsD).toHaveLength(1);
|
||||||
expect(callsD[0][0]).toMatchInlineSnapshot(
|
expect(callsD[0][0]).toMatchInlineSnapshot(
|
||||||
`"http://localhost/api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`,
|
`"http://localhost/api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25,select_columns:!(id,dashboard_title,published,url,slug,changed_by,changed_on_delta_humanized,owners.id,owners.first_name,owners.last_name,owners,tags.id,tags.name,tags.type,status,certified_by,certification_details,changed_on))"`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,27 @@ const Actions = styled.div`
|
|||||||
color: ${({ theme }) => theme.colors.grayscale.base};
|
color: ${({ theme }) => theme.colors.grayscale.base};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const DASHBOARD_COLUMNS_TO_FETCH = [
|
||||||
|
'id',
|
||||||
|
'dashboard_title',
|
||||||
|
'published',
|
||||||
|
'url',
|
||||||
|
'slug',
|
||||||
|
'changed_by',
|
||||||
|
'changed_on_delta_humanized',
|
||||||
|
'owners.id',
|
||||||
|
'owners.first_name',
|
||||||
|
'owners.last_name',
|
||||||
|
'owners',
|
||||||
|
'tags.id',
|
||||||
|
'tags.name',
|
||||||
|
'tags.type',
|
||||||
|
'status',
|
||||||
|
'certified_by',
|
||||||
|
'certification_details',
|
||||||
|
'changed_on',
|
||||||
|
];
|
||||||
|
|
||||||
function DashboardList(props: DashboardListProps) {
|
function DashboardList(props: DashboardListProps) {
|
||||||
const { addDangerToast, addSuccessToast, user } = props;
|
const { addDangerToast, addSuccessToast, user } = props;
|
||||||
|
|
||||||
@@ -135,6 +156,11 @@ function DashboardList(props: DashboardListProps) {
|
|||||||
'dashboard',
|
'dashboard',
|
||||||
t('dashboard'),
|
t('dashboard'),
|
||||||
addDangerToast,
|
addDangerToast,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
DASHBOARD_COLUMNS_TO_FETCH,
|
||||||
);
|
);
|
||||||
const dashboardIds = useMemo(() => dashboards.map(d => d.id), [dashboards]);
|
const dashboardIds = useMemo(() => dashboards.map(d => d.id), [dashboards]);
|
||||||
const [saveFavoriteStatus, favoriteStatus] = useFavoriteStatus(
|
const [saveFavoriteStatus, favoriteStatus] = useFavoriteStatus(
|
||||||
|
|||||||
105
superset-frontend/src/views/CRUD/hooks.test.tsx
Normal file
105
superset-frontend/src/views/CRUD/hooks.test.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* 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 { renderHook } from '@testing-library/react-hooks';
|
||||||
|
import { JsonResponse, SupersetClient } from '@superset-ui/core';
|
||||||
|
|
||||||
|
import { useListViewResource } from './hooks';
|
||||||
|
|
||||||
|
describe('useListViewResource', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch data with correct query parameters', async () => {
|
||||||
|
const pageIndex = 0; // Declare and initialize the pageIndex variable
|
||||||
|
const pageSize = 10; // Declare and initialize the pageSize variable
|
||||||
|
const baseFilters = [{ id: 'status', operator: 'equals', value: 'active' }];
|
||||||
|
|
||||||
|
const fetchSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({
|
||||||
|
json: {
|
||||||
|
result: {
|
||||||
|
dashboard_title: 'New Title',
|
||||||
|
slug: '/new',
|
||||||
|
json_metadata: '{"something":"foo"}',
|
||||||
|
owners: [{ id: 1, first_name: 'Al', last_name: 'Pacino' }],
|
||||||
|
roles: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as JsonResponse);
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useListViewResource('example', 'Example', jest.fn()),
|
||||||
|
);
|
||||||
|
result.current.fetchData({
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
sortBy: [{ id: 'foo' }], // Change the type of sortBy from string to SortColumn[]
|
||||||
|
filters: baseFilters,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetchSpy).toHaveBeenNthCalledWith(2, {
|
||||||
|
endpoint:
|
||||||
|
'/api/v1/example/?q=(filters:!((col:status,opr:equals,value:active)),order_column:foo,order_direction:asc,page:0,page_size:10)',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass the selectColumns to the fetch call', async () => {
|
||||||
|
const pageIndex = 0; // Declare and initialize the pageIndex variable
|
||||||
|
const pageSize = 10; // Declare and initialize the pageSize variable
|
||||||
|
const baseFilters = [{ id: 'status', operator: 'equals', value: 'active' }];
|
||||||
|
const selectColumns = ['id', 'name'];
|
||||||
|
|
||||||
|
const fetchSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({
|
||||||
|
json: {
|
||||||
|
result: {
|
||||||
|
dashboard_title: 'New Title',
|
||||||
|
slug: '/new',
|
||||||
|
json_metadata: '{"something":"foo"}',
|
||||||
|
owners: [{ id: 1, first_name: 'Al', last_name: 'Pacino' }],
|
||||||
|
roles: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as JsonResponse);
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useListViewResource(
|
||||||
|
'example',
|
||||||
|
'Example',
|
||||||
|
jest.fn(),
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
selectColumns,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
result.current.fetchData({
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
sortBy: [{ id: 'foo' }], // Change the type of sortBy from string to SortColumn[]
|
||||||
|
filters: baseFilters,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetchSpy).toHaveBeenNthCalledWith(2, {
|
||||||
|
endpoint:
|
||||||
|
'/api/v1/example/?q=(filters:!((col:status,opr:equals,value:active)),order_column:foo,order_direction:asc,page:0,page_size:10,select_columns:!(id,name))',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -77,6 +77,7 @@ export function useListViewResource<D extends object = any>(
|
|||||||
defaultCollectionValue: D[] = [],
|
defaultCollectionValue: D[] = [],
|
||||||
baseFilters?: FilterValue[], // must be memoized
|
baseFilters?: FilterValue[], // must be memoized
|
||||||
initialLoadingState = true,
|
initialLoadingState = true,
|
||||||
|
selectColumns?: string[],
|
||||||
) {
|
) {
|
||||||
const [state, setState] = useState<ListViewResourceState<D>>({
|
const [state, setState] = useState<ListViewResourceState<D>>({
|
||||||
count: 0,
|
count: 0,
|
||||||
@@ -162,6 +163,7 @@ export function useListViewResource<D extends object = any>(
|
|||||||
page: pageIndex,
|
page: pageIndex,
|
||||||
page_size: pageSize,
|
page_size: pageSize,
|
||||||
...(filterExps.length ? { filters: filterExps } : {}),
|
...(filterExps.length ? { filters: filterExps } : {}),
|
||||||
|
...(selectColumns?.length ? { select_columns: selectColumns } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return SupersetClient.get({
|
return SupersetClient.get({
|
||||||
|
|||||||
Reference in New Issue
Block a user