feat: Reuse Dashboard redux data in Explore (#20668)

This commit is contained in:
Kamil Gabryjelski 2022-07-12 19:17:25 +02:00 committed by GitHub
parent c795dc23b9
commit ff5b4bc0e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 8 deletions

View File

@ -17,6 +17,6 @@
* under the License. * under the License.
*/ */
export default function isDefined(x: unknown) { export default function isDefined<T>(x: T): x is NonNullable<T> {
return x !== null && x !== undefined; return x !== null && x !== undefined;
} }

View File

@ -150,9 +150,13 @@ export const hydrateDashboard =
datasource: slice.form_data.datasource, datasource: slice.form_data.datasource,
description: slice.description, description: slice.description,
description_markeddown: slice.description_markeddown, description_markeddown: slice.description_markeddown,
owners: slice.owners, owners: slice.owners.map(owner => owner.id),
modified: slice.modified, modified: slice.modified,
changed_on: new Date(slice.changed_on).getTime(), changed_on: new Date(slice.changed_on).getTime(),
is_managed_externally: slice.is_managed_externally,
query_context: slice.query_context,
certified_by: slice.certified_by,
certification_details: slice.certification_details,
}; };
sliceIds.add(key); sliceIds.add(key);

View File

@ -302,7 +302,7 @@ class Chart extends React.Component {
) { ) {
window.open(url, '_blank', 'noreferrer'); window.open(url, '_blank', 'noreferrer');
} else { } else {
this.props.history.push(url); this.props.history.push(url, { dashboardId: this.props.dashboardId });
} }
} catch (error) { } catch (error) {
logging.error(error); logging.error(error);

View File

@ -16,10 +16,13 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { makeApi, t } from '@superset-ui/core'; import { isDefined, makeApi, SupersetClient, t } from '@superset-ui/core';
import { useLocation } from 'react-router-dom';
import Loading from 'src/components/Loading'; import Loading from 'src/components/Loading';
import pick from 'lodash/pick';
import { Dataset } from '@superset-ui/chart-controls';
import { getParsedExploreURLParams } from './exploreUtils/getParsedExploreURLParams'; import { getParsedExploreURLParams } from './exploreUtils/getParsedExploreURLParams';
import { hydrateExplore } from './actions/hydrateExplore'; import { hydrateExplore } from './actions/hydrateExplore';
import ExploreViewContainer from './components/ExploreViewContainer'; import ExploreViewContainer from './components/ExploreViewContainer';
@ -27,6 +30,10 @@ import { ExploreResponsePayload } from './types';
import { fallbackExploreInitialData } from './fixtures'; import { fallbackExploreInitialData } from './fixtures';
import { addDangerToast } from '../components/MessageToasts/actions'; import { addDangerToast } from '../components/MessageToasts/actions';
import { isNullish } from '../utils/common'; import { isNullish } from '../utils/common';
import { getUrlParam } from '../utils/urlUtils';
import { URL_PARAMS } from '../constants';
import { RootState } from '../dashboard/types';
import { Slice } from '../types/Chart';
const loadErrorMessage = t('Failed to load chart data.'); const loadErrorMessage = t('Failed to load chart data.');
@ -38,12 +45,58 @@ const fetchExploreData = () => {
})(exploreUrlParams); })(exploreUrlParams);
}; };
const useExploreInitialData = (
shouldUseDashboardData: boolean,
sliceId: string | null,
) => {
const slice = useSelector<RootState, Slice | null>(({ sliceEntities }) =>
isDefined(sliceId) ? sliceEntities?.slices?.[sliceId] : null,
);
const formData = slice?.form_data;
const { id: datasourceId, type: datasourceType } = useSelector<
RootState,
{ id: number | undefined; type: string | undefined }
>(({ datasources }) =>
formData?.datasource
? pick(datasources[formData.datasource], ['id', 'type'])
: { id: undefined, type: undefined },
);
return useCallback(() => {
if (
!shouldUseDashboardData ||
!isDefined(slice) ||
!isDefined(formData) ||
!isDefined(datasourceId) ||
!isDefined(datasourceType)
) {
return fetchExploreData();
}
return SupersetClient.get({
endpoint: `/datasource/get/${datasourceType}/${datasourceId}/`,
}).then(({ json }) => ({
result: {
slice,
form_data: formData,
dataset: json as Dataset,
message: '',
},
}));
/* eslint-disable react-hooks/exhaustive-deps */
}, []);
};
const ExplorePage = () => { const ExplorePage = () => {
const [isLoaded, setIsLoaded] = useState(false); const [isLoaded, setIsLoaded] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const location = useLocation<{ dashboardId?: number }>();
const getExploreInitialData = useExploreInitialData(
isDefined(location.state?.dashboardId),
getUrlParam(URL_PARAMS.sliceId),
);
useEffect(() => { useEffect(() => {
fetchExploreData() getExploreInitialData()
.then(({ result }) => { .then(({ result }) => {
if (isNullish(result.dataset?.id) && isNullish(result.dataset?.uid)) { if (isNullish(result.dataset?.id) && isNullish(result.dataset?.uid)) {
dispatch(hydrateExplore(fallbackExploreInitialData)); dispatch(hydrateExplore(fallbackExploreInitialData));

View File

@ -56,7 +56,7 @@ export const hydrateExplore =
initialFormData.dashboardId = dashboardId; initialFormData.dashboardId = dashboardId;
} }
const initialDatasource = const initialDatasource =
datasources?.[initialFormData.datasource] ?? dataset; dataset ?? datasources?.[initialFormData.datasource];
const initialExploreState = { const initialExploreState = {
form_data: initialFormData, form_data: initialFormData,

View File

@ -121,6 +121,7 @@ description_markeddown_description = "Sanitized HTML version of the chart descri
owners_name_description = "Name of an owner of the chart." owners_name_description = "Name of an owner of the chart."
certified_by_description = "Person or group that has certified this chart" certified_by_description = "Person or group that has certified this chart"
certification_details_description = "Details of the certification" certification_details_description = "Details of the certification"
is_managed_externally_description = "If the chart is managed outside externally"
# #
# OpenAPI method specification overrides # OpenAPI method specification overrides
@ -148,6 +149,10 @@ openapi_spec_methods_override = {
} }
class UserSchema(Schema):
id = fields.Int()
class ChartEntityResponseSchema(Schema): class ChartEntityResponseSchema(Schema):
""" """
Schema for a chart object Schema for a chart object
@ -165,6 +170,11 @@ class ChartEntityResponseSchema(Schema):
slice_url = fields.String(description=slice_url_description) slice_url = fields.String(description=slice_url_description)
certified_by = fields.String(description=certified_by_description) certified_by = fields.String(description=certified_by_description)
certification_details = fields.String(description=certification_details_description) certification_details = fields.String(description=certification_details_description)
is_managed_externally = fields.Boolean(
description=is_managed_externally_description
)
owners = fields.List(fields.Nested(UserSchema))
query_context = fields.String(description=query_context_description)
class ChartPostSchema(Schema): class ChartPostSchema(Schema):