Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(homepage): separate out api calls to make homepage load more dynamically #13500

Merged
merged 9 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
*/
import React from 'react';
import { styledMount as mount } from 'spec/helpers/theming';
import { act } from 'react-dom/test-utils';
import { ReactWrapper } from 'enzyme';
import { Provider } from 'react-redux';
import fetchMock from 'fetch-mock';
import thunk from 'redux-thunk';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import configureStore from 'redux-mock-store';
Expand All @@ -26,6 +30,9 @@ import ActivityTable from 'src/views/CRUD/welcome/ActivityTable';
const mockStore = configureStore([thunk]);
const store = mockStore({});

const chartsEndpoint = 'glob:*/api/v1/chart/?*';
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';

const mockData = {
Viewed: [
{
Expand All @@ -36,38 +43,58 @@ const mockData = {
table: {},
},
],
Edited: [
Created: [
{
dashboard_title: 'Dashboard_Test',
changed_on_utc: '24 Feb 2014 10:13:14',
url: '/fakeUrl/dashboard',
id: '3',
},
],
Created: [
};

fetchMock.get(chartsEndpoint, {
result: [
{
slice_name: 'ChartyChart',
changed_on_utc: '24 Feb 2014 10:13:14',
url: '/fakeUrl/explore',
id: '4',
table: {},
},
],
});

fetchMock.get(dashboardsEndpoint, {
result: [
{
dashboard_title: 'Dashboard_Test',
changed_on_utc: '24 Feb 2014 10:13:14',
url: '/fakeUrl/dashboard',
id: '3',
},
],
};
});

describe('ActivityTable', () => {
const activityProps = {
activeChild: 'Edited',
activeChild: 'Created',
activityData: mockData,
setActiveChild: jest.fn(),
user: { userId: '1' },
loading: false,
};
const wrapper = mount(<ActivityTable {...activityProps} />, {
context: { store },
});

let wrapper: ReactWrapper;

beforeAll(async () => {
await waitForComponentToPaint(wrapper);
await act(async () => {
wrapper = mount(
<Provider store={store}>
<ActivityTable {...activityProps} />
</Provider>,
);
});
});

it('the component renders', () => {
Expand All @@ -79,4 +106,32 @@ describe('ActivityTable', () => {
it('renders ActivityCards', async () => {
expect(wrapper.find('ListViewCard')).toExist();
});
it('calls the getEdited batch call when edited tab is clicked', async () => {
act(() => {
const handler = wrapper.find('li.no-router a').at(1).prop('onClick');
if (handler) {
handler({} as any);
}
});
await waitForComponentToPaint(wrapper);
const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
const chartCall = fetchMock.calls(/chart\/\?q/);
expect(chartCall).toHaveLength(1);
expect(dashboardCall).toHaveLength(1);
});
it('show empty state if there is data', () => {
const activityProps = {
activeChild: 'Created',
activityData: {},
setActiveChild: jest.fn(),
user: { userId: '1' },
loading: false,
};
const wrapper = mount(
<Provider store={store}>
<ActivityTable {...activityProps} />
</Provider>,
);
expect(wrapper.find('EmptyState')).toExist();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ fetchMock.get(savedQueryEndpoint, {
result: [],
});

fetchMock.get(recentActivityEndpoint, {});
fetchMock.get(recentActivityEndpoint, {
Created: [],
Viewed: [],
});

fetchMock.get(chartInfoEndpoint, {
permissions: [],
Expand Down Expand Up @@ -122,10 +125,14 @@ describe('Welcome', () => {
expect(wrapper.find('CollapsePanel')).toHaveLength(8);
});

it('calls batch method on page load', () => {
it('calls api methods in parallel on page load', () => {
const chartCall = fetchMock.calls(/chart\/\?q/);
const savedQueryCall = fetchMock.calls(/saved_query\/\?q/);
const recentCall = fetchMock.calls(/superset\/recent_activity\/*/);
const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
expect(chartCall).toHaveLength(2);
expect(dashboardCall).toHaveLength(2);
expect(chartCall).toHaveLength(1);
expect(recentCall).toHaveLength(1);
expect(savedQueryCall).toHaveLength(1);
expect(dashboardCall).toHaveLength(1);
});
});
4 changes: 3 additions & 1 deletion superset-frontend/src/views/CRUD/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface ListViewResourceState<D extends object = any> {
lastFetchDataConfig: FetchDataConfig | null;
bulkSelectEnabled: boolean;
lastFetched?: string;
defualtLoading?: boolean;
}

export function useListViewResource<D extends object = any>(
Expand All @@ -45,11 +46,12 @@ export function useListViewResource<D extends object = any>(
infoEnable = true,
defaultCollectionValue: D[] = [],
baseFilters?: FilterValue[], // must be memoized
defaultLoading = true,
) {
const [state, setState] = useState<ListViewResourceState<D>>({
count: 0,
collection: defaultCollectionValue,
loading: true,
loading: defaultLoading,
lastFetchDataConfig: null,
permissions: [],
bulkSelectEnabled: false,
Expand Down
146 changes: 71 additions & 75 deletions superset-frontend/src/views/CRUD/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { FetchDataConfig } from 'src/components/ListView';
import { Dashboard } from './types';

interface Filters {
col: string;
opr: string;
value: string;
}

const createFetchResourceMethod = (method: string) => (
resource: string,
relation: string,
Expand Down Expand Up @@ -61,102 +67,92 @@ const createFetchResourceMethod = (method: string) => (
return [];
};

export const getRecentAcitivtyObjs = (
userId: string | number,
recent: string,
addDangerToast: (arg1: string, arg2: any) => any,
) => {
const getParams = (filters?: Array<any>) => {
const params = {
order_column: 'changed_on_delta_humanized',
order_direction: 'desc',
page: 0,
page_size: 3,
filters,
};
if (!filters) delete params.filters;
return rison.encode(params);
const getParams = (filters?: Array<Filters>) => {
const params = {
order_column: 'changed_on_delta_humanized',
order_direction: 'desc',
page: 0,
page_size: 3,
filters,
};
if (!filters) delete params.filters;
return rison.encode(params);
};

export const getEditedObjs = (userId: string | number) => {
const filters = {
// chart and dashbaord uses same filters
// for edited and created
edited: [
{
col: 'changed_by',
opr: 'rel_o_m',
value: `${userId}`,
},
],
created: [
{
col: 'created_by',
opr: 'rel_o_m',
value: `${userId}`,
},
],
};
const baseBatch = [
SupersetClient.get({ endpoint: recent }),
const batch = [
SupersetClient.get({
endpoint: `/api/v1/dashboard/?q=${getParams(filters.edited)}`,
}),
SupersetClient.get({
endpoint: `/api/v1/chart/?q=${getParams(filters.edited)}`,
}),
SupersetClient.get({
endpoint: `/api/v1/dashboard/?q=${getParams(filters.created)}`,
}),
SupersetClient.get({
endpoint: `/api/v1/chart/?q=${getParams(filters.created)}`,
}),
SupersetClient.get({
endpoint: `/api/v1/saved_query/?q=${getParams(filters.created)}`,
}),
];
return Promise.all(baseBatch).then(
([
recentsRes,
editedDash,
editedChart,
createdByDash,
createdByChart,
createdByQuery,
]) => {
const res: any = {
editedDash: editedDash.json?.result.slice(0, 3),
editedChart: editedChart.json?.result.slice(0, 3),
createdByDash: createdByDash.json?.result.slice(0, 3),
createdByChart: createdByChart.json?.result.slice(0, 3),
createdByQuery: createdByQuery.json?.result.slice(0, 3),
return Promise.all(batch)
.then(([editedCharts, editedDashboards]) => {
const res = {
editedDash: editedDashboards.json?.result.slice(0, 3),
editedChart: editedCharts.json?.result.slice(0, 3),
};
if (recentsRes.json.length === 0) {
const newBatch = [
SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams()}` }),
SupersetClient.get({
endpoint: `/api/v1/dashboard/?q=${getParams()}`,
}),
];
return Promise.all(newBatch)
.then(([chartRes, dashboardRes]) => {
res.examples = [
...chartRes.json.result,
...dashboardRes.json.result,
];
return res;
})
.catch(errMsg =>
addDangerToast(
t('There was an error fetching your recent activity:'),
errMsg,
),
);
}
res.viewed = recentsRes.json;
return res;
},
);
})
.catch(err => err);
};

export const getMineObjs = (userId: string | number, resource: string) => {
const filters = {
created: [
{
col: 'created_by',
opr: 'rel_o_m',
value: `${userId}`,
},
],
};
return SupersetClient.get({
endpoint: `/api/v1/${resource}/?q=${getParams(filters.created)}`,
}).then(res => res.json?.result);
};

export const getRecentAcitivtyObjs = (
userId: string | number,
recent: string,
addDangerToast: (arg1: string, arg2: any) => any,
) =>
SupersetClient.get({ endpoint: recent }).then(recentsRes => {
const res: any = {};
if (recentsRes.json.length === 0) {
const newBatch = [
SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams()}` }),
SupersetClient.get({
endpoint: `/api/v1/dashboard/?q=${getParams()}`,
}),
];
return Promise.all(newBatch)
.then(([chartRes, dashboardRes]) => {
res.examples = [...chartRes.json.result, ...dashboardRes.json.result];
return res;
})
.catch(errMsg =>
addDangerToast(
t('There was an error fetching your recent activity:'),
errMsg,
),
);
}
res.viewed = recentsRes.json;
return res;
});

export const createFetchRelated = createFetchResourceMethod('related');
export const createFetchDistinct = createFetchResourceMethod('distinct');

Expand Down
Loading