diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.js b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.js index c279cc5f093..913609cdc9a 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.js +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.js @@ -28,11 +28,12 @@ export default () => cy.get('#app').then(data => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const dashboard = bootstrapData.dashboard_data; + const dashboardId = dashboard.id; const boxplotChartId = dashboard.slices.find( slice => slice.form_data.viz_type === 'box_plot', ).slice_id; const formData = `{"slice_id":${boxplotChartId}}`; - const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; + const boxplotRequest = `/superset/explore_json/?form_data=${formData}&dashboard_id=${dashboardId}`; cy.route('POST', boxplotRequest).as('boxplotRequest'); }); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/filter.js b/superset-frontend/cypress-base/cypress/integration/dashboard/filter.js index 6da7de22d3e..c219796e325 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/filter.js +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/filter.js @@ -22,6 +22,7 @@ export default () => describe('dashboard filter', () => { let sliceIds = []; let filterId; + let dashboardId; beforeEach(() => { cy.server(); @@ -32,6 +33,7 @@ export default () => cy.get('#app').then(data => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const dashboard = bootstrapData.dashboard_data; + dashboardId = dashboard.id; sliceIds = dashboard.slices.map(slice => slice.slice_id); filterId = dashboard.slices.find( slice => slice.form_data.viz_type === 'filter_box', @@ -43,7 +45,7 @@ export default () => const aliases = []; const formData = `{"slice_id":${filterId}}`; - const filterRoute = `/superset/explore_json/?form_data=${formData}`; + const filterRoute = `/superset/explore_json/?form_data=${formData}&dashboard_id=${dashboardId}`; cy.route('POST', filterRoute).as('fetchFilter'); cy.wait('@fetchFilter'); sliceIds @@ -54,7 +56,7 @@ export default () => cy.route( 'POST', - `/superset/explore_json/?form_data={"slice_id":${id}}`, + `/superset/explore_json/?form_data={"slice_id":${id}}&dashboard_id=${dashboardId}`, ).as(alias); }); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/load.js b/superset-frontend/cypress-base/cypress/integration/dashboard/load.js index 6050cbf55c2..5cb64fe1afd 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/load.js +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/load.js @@ -31,14 +31,16 @@ export default () => cy.get('#app').then(data => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); + const dashboardId = bootstrapData.dashboard_data.id; const slices = bootstrapData.dashboard_data.slices; // then define routes and create alias for each requests slices.forEach(slice => { const alias = `getJson_${slice.slice_id}`; const formData = `{"slice_id":${slice.slice_id}}`; - cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as( - alias, - ); + cy.route( + 'POST', + `/superset/explore_json/?form_data=${formData}&dashboard_id=${dashboardId}`, + ).as(alias); aliases.push(`@${alias}`); }); }); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/save.js b/superset-frontend/cypress-base/cypress/integration/dashboard/save.js index 263fd8addfd..03dec465bd9 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/save.js +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/save.js @@ -60,11 +60,9 @@ export default () => }); it('should save/overwrite dashboard', () => { - cy.wait('@copyRequest'); - // should have box_plot chart const formData = `{"slice_id":${boxplotChartId}}`; - const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; + const boxplotRequest = `/superset/explore_json/?form_data=${formData}&dashboard_id=${dashboardId}`; cy.route('POST', boxplotRequest).as('boxplotRequest'); cy.wait('@boxplotRequest'); cy.get('.grid-container .box_plot').should('be.exist'); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.js b/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.js index 4a86d3c703f..51dfa54d231 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.js +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/tabs.js @@ -24,6 +24,7 @@ export default () => let treemapId; let linechartId; let boxplotId; + let dashboardId; // cypress can not handle window.scrollTo // https://github.com/cypress-io/cypress/issues/2761 @@ -43,6 +44,7 @@ export default () => cy.get('#app').then(data => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const dashboard = bootstrapData.dashboard_data; + dashboardId = dashboard.id; filterId = dashboard.slices.find( slice => slice.form_data.viz_type === 'filter_box', ).slice_id; @@ -61,7 +63,7 @@ export default () => }; const filterRequest = `/superset/explore_json/?form_data=${JSON.stringify( filterFormdata, - )}`; + )}&dashboard_id=${dashboardId}`; cy.route('POST', filterRequest).as('filterRequest'); const treemapFormdata = { @@ -69,7 +71,7 @@ export default () => }; const treemapRequest = `/superset/explore_json/?form_data=${JSON.stringify( treemapFormdata, - )}`; + )}&dashboard_id=${dashboardId}`; cy.route('POST', treemapRequest).as('treemapRequest'); const linechartFormdata = { @@ -77,7 +79,7 @@ export default () => }; const linechartRequest = `/superset/explore_json/?form_data=${JSON.stringify( linechartFormdata, - )}`; + )}&dashboard_id=${dashboardId}`; cy.route('POST', linechartRequest).as('linechartRequest'); const boxplotFormdata = { @@ -85,7 +87,7 @@ export default () => }; const boxplotRequest = `/superset/explore_json/?form_data=${JSON.stringify( boxplotFormdata, - )}`; + )}&dashboard_id=${dashboardId}`; cy.route('POST', boxplotRequest).as('boxplotRequest'); }); }); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/url_params.js b/superset-frontend/cypress-base/cypress/integration/dashboard/url_params.js index d7f983e7c11..54b74a6f7c8 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/url_params.js +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/url_params.js @@ -22,6 +22,7 @@ export default () => describe('dashboard url params', () => { const urlParams = { param1: '123', param2: 'abc' }; let sliceIds = []; + let dashboardId; beforeEach(() => { cy.server(); @@ -32,6 +33,7 @@ export default () => cy.get('#app').then(data => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const dashboard = bootstrapData.dashboard_data; + dashboardId = dashboard.id; sliceIds = dashboard.slices.map(slice => slice.slice_id); }); }); @@ -43,7 +45,7 @@ export default () => aliases.push(`@${alias}`); cy.route( 'POST', - `/superset/explore_json/?form_data={"slice_id":${id}}`, + `/superset/explore_json/?form_data={"slice_id":${id}}&dashboard_id=${dashboardId}`, ).as(alias); }); diff --git a/superset-frontend/src/chart/Chart.jsx b/superset-frontend/src/chart/Chart.jsx index 7a4679c6383..227b7af86b4 100644 --- a/superset-frontend/src/chart/Chart.jsx +++ b/superset-frontend/src/chart/Chart.jsx @@ -34,6 +34,8 @@ const propTypes = { actions: PropTypes.object, chartId: PropTypes.number.isRequired, datasource: PropTypes.object.isRequired, + // current chart is included by dashboard + dashboardId: PropTypes.number, // original selected values for FilterBox viz // so that FilterBox can pre-populate selected values // only affect UI control @@ -71,6 +73,7 @@ const defaultProps = { initialValues: BLANK, setControlValue() {}, triggerRender: false, + dashboardId: null, }; class Chart extends React.PureComponent { @@ -101,6 +104,7 @@ class Chart extends React.PureComponent { false, this.props.timeout, this.props.chartId, + this.props.dashboardId, ); } else { // Create chart with POST request @@ -109,6 +113,7 @@ class Chart extends React.PureComponent { false, this.props.timeout, this.props.chartId, + this.props.dashboardId, ); } } diff --git a/superset-frontend/src/chart/chartAction.js b/superset-frontend/src/chart/chartAction.js index 657d0be8c16..c7a125f6316 100644 --- a/superset-frontend/src/chart/chartAction.js +++ b/superset-frontend/src/chart/chartAction.js @@ -207,6 +207,7 @@ export function exploreJSON( timeout = 60, key, method, + dashboardId, ) { return dispatch => { const { url, payload } = getExploreUrlAndPayload({ @@ -215,6 +216,7 @@ export function exploreJSON( force, allowDomainSharding, method, + requestParams: dashboardId ? { dashboard_id: dashboardId } : {}, }); const logStart = Logger.getTimestamp(); const controller = new AbortController(); @@ -308,7 +310,13 @@ export function exploreJSON( } export const GET_SAVED_CHART = 'GET_SAVED_CHART'; -export function getSavedChart(formData, force = false, timeout = 60, key) { +export function getSavedChart( + formData, + force = false, + timeout = 60, + key, + dashboardId, +) { /* * Perform a GET request to `/explore_json`. * @@ -319,18 +327,24 @@ export function getSavedChart(formData, force = false, timeout = 60, key) { * GET /explore_json?{"chart_id":1,"extra_filters":"..."} * */ - return exploreJSON(formData, force, timeout, key, 'GET'); + return exploreJSON(formData, force, timeout, key, 'GET', dashboardId); } export const POST_CHART_FORM_DATA = 'POST_CHART_FORM_DATA'; -export function postChartFormData(formData, force = false, timeout = 60, key) { +export function postChartFormData( + formData, + force = false, + timeout = 60, + key, + dashboardId, +) { /* * Perform a POST request to `/explore_json`. * * This will post the form data to the endpoint, returning a new chart. * */ - return exploreJSON(formData, force, timeout, key, 'POST'); + return exploreJSON(formData, force, timeout, key, 'POST', dashboardId); } export function redirectSQLLab(formData) { @@ -359,7 +373,7 @@ export function redirectSQLLab(formData) { }; } -export function refreshChart(chartKey, force) { +export function refreshChart(chartKey, force, dashboardId) { return (dispatch, getState) => { const chart = (getState().charts || {})[chartKey]; const timeout = getState().dashboardInfo.common.conf @@ -372,7 +386,13 @@ export function refreshChart(chartKey, force) { return; } dispatch( - postChartFormData(chart.latestQueryFormData, force, timeout, chart.id), + postChartFormData( + chart.latestQueryFormData, + force, + timeout, + chart.id, + dashboardId, + ), ); }; } diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js index 20bb35dfcee..33737f83bd6 100644 --- a/superset-frontend/src/dashboard/actions/dashboardState.js +++ b/superset-frontend/src/dashboard/actions/dashboardState.js @@ -218,10 +218,17 @@ export function saveDashboardRequest(data, id, saveType) { }; } -export function fetchCharts(chartList = [], force = false, interval = 0) { +export function fetchCharts( + chartList = [], + force = false, + interval = 0, + dashboardId, +) { return (dispatch, getState) => { if (!interval) { - chartList.forEach(chartKey => dispatch(refreshChart(chartKey, force))); + chartList.forEach(chartKey => + dispatch(refreshChart(chartKey, force, dashboardId)), + ); return; } @@ -237,7 +244,10 @@ export function fetchCharts(chartList = [], force = false, interval = 0) { ? refreshTime / (chartList.length - 1) : 0; chartList.forEach((chartKey, i) => { - setTimeout(() => dispatch(refreshChart(chartKey, force)), delay * i); + setTimeout( + () => dispatch(refreshChart(chartKey, force, dashboardId)), + delay * i, + ); }); }; } diff --git a/superset-frontend/src/dashboard/components/Header.jsx b/superset-frontend/src/dashboard/components/Header.jsx index 4c8d01462a4..c889d063cc3 100644 --- a/superset-frontend/src/dashboard/components/Header.jsx +++ b/superset-frontend/src/dashboard/components/Header.jsx @@ -194,7 +194,13 @@ class Header extends React.PureComponent { interval: 0, chartCount: chartList.length, }); - return this.props.fetchCharts(chartList, true); + + return this.props.fetchCharts( + chartList, + true, + 0, + this.props.dashboardInfo.id, + ); } return false; } @@ -212,7 +218,12 @@ class Header extends React.PureComponent { interval, chartCount: affectedCharts.length, }); - return fetchCharts(affectedCharts, true, interval * 0.2); + return fetchCharts( + affectedCharts, + true, + interval * 0.2, + dashboardInfo.id, + ); }; this.refreshTimer = setPeriodicRunner({ diff --git a/superset-frontend/src/dashboard/components/SliceHeader.jsx b/superset-frontend/src/dashboard/components/SliceHeader.jsx index 3d36a51e259..a1fb9f39d89 100644 --- a/superset-frontend/src/dashboard/components/SliceHeader.jsx +++ b/superset-frontend/src/dashboard/components/SliceHeader.jsx @@ -44,6 +44,7 @@ const propTypes = { supersetCanCSV: PropTypes.bool, sliceCanEdit: PropTypes.bool, componentId: PropTypes.string.isRequired, + dashboardId: PropTypes.number.isRequired, filters: PropTypes.object.isRequired, addDangerToast: PropTypes.func.isRequired, }; @@ -94,6 +95,7 @@ class SliceHeader extends React.PureComponent { annotationQuery, annotationError, componentId, + dashboardId, addDangerToast, } = this.props; @@ -145,6 +147,7 @@ class SliceHeader extends React.PureComponent { supersetCanCSV={supersetCanCSV} sliceCanEdit={sliceCanEdit} componentId={componentId} + dashboardId={dashboardId} addDangerToast={addDangerToast} /> )} diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx b/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx index 779609d850c..6bd50610cf2 100644 --- a/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx +++ b/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx @@ -28,6 +28,7 @@ import { getActiveFilters } from '../util/activeDashboardFilters'; const propTypes = { slice: PropTypes.object.isRequired, componentId: PropTypes.string.isRequired, + dashboardId: PropTypes.number.isRequired, addDangerToast: PropTypes.func.isRequired, isCached: PropTypes.bool, isExpanded: PropTypes.bool, @@ -91,7 +92,10 @@ class SliceHeaderControls extends React.PureComponent { refreshChart() { if (this.props.updatedDttm) { - this.props.forceRefresh(this.props.slice.slice_id); + this.props.forceRefresh( + this.props.slice.slice_id, + this.props.dashboardId, + ); } } diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx index 02932cac8d2..b3995284b4e 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx @@ -36,6 +36,7 @@ import getFilterValuesByFilterId from '../../util/getFilterValuesByFilterId'; const propTypes = { id: PropTypes.number.isRequired, componentId: PropTypes.string.isRequired, + dashboardId: PropTypes.number.isRequired, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, updateSliceName: PropTypes.func.isRequired, @@ -205,13 +206,18 @@ class Chart extends React.Component { slice_id: this.props.slice.slice_id, is_cached: this.props.isCached, }); - return this.props.refreshChart(this.props.chart.id, true); + return this.props.refreshChart( + this.props.chart.id, + true, + this.props.dashboardId, + ); } render() { const { id, componentId, + dashboardId, chart, slice, datasource, @@ -269,6 +275,7 @@ class Chart extends React.Component { supersetCanCSV={supersetCanCSV} sliceCanEdit={sliceCanEdit} componentId={componentId} + dashboardId={dashboardId} filters={filters} addDangerToast={addDangerToast} /> @@ -306,6 +313,7 @@ class Chart extends React.Component { chartId={id} chartStatus={chart.chartStatus} datasource={datasource} + dashboardId={dashboardId} initialValues={initialValues} formData={formData} queryResponse={chart.queryResponse} diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.jsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.jsx index a3cf5c1ce3a..85d5fdfaf0d 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.jsx @@ -42,6 +42,7 @@ const CHART_MARGIN = 32; const propTypes = { id: PropTypes.string.isRequired, parentId: PropTypes.string.isRequired, + dashboardId: PropTypes.number.isRequired, component: componentShape.isRequired, parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, @@ -175,6 +176,7 @@ class ChartHolder extends React.Component { handleComponentDrop, editMode, isComponentVisible, + dashboardId, } = this.props; // inherit the size of parent columns @@ -228,6 +230,7 @@ class ChartHolder extends React.Component {