This is an automated email from the ASF dual-hosted git repository.

robdiciuccio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 49ec13c  feat: Support multiple queries per request (#11880)
49ec13c is described below

commit 49ec13c68c183b110b5c7ad18dfb829ce9f34245
Author: simchaNielsen <[email protected]>
AuthorDate: Fri Dec 18 19:15:27 2020 +0200

    feat: Support multiple queries per request (#11880)
    
    * refactor: add queriesData fields for multiple queries
    
    * feat: support multi queries request
    
    * lint: fix lint
    
    * lint: fix lint
    
    * lint: fix lint
    
    * fix: fix CR notes
    
    * fix: fix CR notes
    
    * fix: fix CR notes
    
    * fix: fix error case for multi queries
    
    * feat: change queryResponse to queriesResponse
    
    * fix: revert webpack
    
    * test: fix tests
    
    * chore: lint
    
    * chore: adjust asyncEvent to multiple results
    
    * fix: lint
    
    * fix: eslint
    
    * fix: another eslint rule
    
    Co-authored-by: Amit Miran <[email protected]>
    Co-authored-by: amitmiran137 <[email protected]>
---
 .../spec/javascripts/chart/chartActions_spec.js    |  2 +-
 .../dashboard/components/FiltersBadge_spec.tsx     | 36 ++++++++------
 superset-frontend/src/chart/Chart.jsx              | 16 ++----
 superset-frontend/src/chart/ChartRenderer.jsx      | 13 ++---
 superset-frontend/src/chart/chartAction.js         | 57 ++++++++++------------
 superset-frontend/src/chart/chartReducer.js        | 14 +++---
 .../dashboard/components/FiltersBadge/selectors.ts |  4 +-
 .../src/dashboard/components/SliceHeader.jsx       |  8 +--
 .../dashboard/components/SliceHeaderControls.jsx   | 38 +++++++++++----
 .../dashboard/components/gridComponents/Chart.jsx  | 12 +++--
 .../src/dashboard/util/propShapes.jsx              |  2 +-
 .../src/explore/components/DisplayQueryButton.jsx  |  1 -
 .../explore/components/ExploreActionButtons.jsx    |  6 +--
 .../src/explore/components/ExploreChartHeader.jsx  |  4 +-
 .../src/explore/components/ExploreChartPanel.jsx   |  2 +-
 .../src/explore/reducers/getInitialState.js        |  2 +-
 superset-frontend/src/middleware/asyncEvent.ts     | 15 +++---
 17 files changed, 127 insertions(+), 105 deletions(-)

diff --git a/superset-frontend/spec/javascripts/chart/chartActions_spec.js 
b/superset-frontend/spec/javascripts/chart/chartActions_spec.js
index dc57e22..7bca9f7 100644
--- a/superset-frontend/spec/javascripts/chart/chartActions_spec.js
+++ b/superset-frontend/spec/javascripts/chart/chartActions_spec.js
@@ -189,7 +189,7 @@ describe('chart actions', () => {
         expect(dispatch.callCount).toBe(5);
         const updateFailedAction = dispatch.args[4][0];
         expect(updateFailedAction.type).toBe(actions.CHART_UPDATE_FAILED);
-        expect(updateFailedAction.queryResponse.error).toBe('misc error');
+        expect(updateFailedAction.queriesResponse[0].error).toBe('misc error');
 
         setupDefaultFetchMock();
       });
diff --git 
a/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx 
b/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx
index 07dfdd5..7a53de0 100644
--- 
a/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx
+++ 
b/superset-frontend/spec/javascripts/dashboard/components/FiltersBadge_spec.tsx
@@ -51,11 +51,13 @@ describe('FiltersBadge', () => {
     store.dispatch({
       type: CHART_UPDATE_SUCCEEDED,
       key: sliceId,
-      queryResponse: {
-        status: 'success',
-        applied_filters: [],
-        rejected_filters: [],
-      },
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [],
+          rejected_filters: [],
+        },
+      ],
       dashboardFilters,
     });
     const wrapper = shallow(
@@ -74,11 +76,13 @@ describe('FiltersBadge', () => {
     store.dispatch({
       type: CHART_UPDATE_SUCCEEDED,
       key: sliceId,
-      queryResponse: {
-        status: 'success',
-        applied_filters: [{ column: 'region' }],
-        rejected_filters: [],
-      },
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [{ column: 'region' }],
+          rejected_filters: [],
+        },
+      ],
       dashboardFilters,
     });
     const wrapper = shallow(
@@ -97,11 +101,13 @@ describe('FiltersBadge', () => {
     store.dispatch({
       type: CHART_UPDATE_SUCCEEDED,
       key: sliceId,
-      queryResponse: {
-        status: 'success',
-        applied_filters: [],
-        rejected_filters: [{ column: 'region', reason: 'not_in_datasource' }],
-      },
+      queriesResponse: [
+        {
+          status: 'success',
+          applied_filters: [],
+          rejected_filters: [{ column: 'region', reason: 'not_in_datasource' 
}],
+        },
+      ],
       dashboardFilters,
     });
     const wrapper = shallow(
diff --git a/superset-frontend/src/chart/Chart.jsx 
b/superset-frontend/src/chart/Chart.jsx
index bb4910c..20b3b98 100644
--- a/superset-frontend/src/chart/Chart.jsx
+++ b/superset-frontend/src/chart/Chart.jsx
@@ -56,7 +56,7 @@ const propTypes = {
   chartAlert: PropTypes.string,
   chartStatus: PropTypes.string,
   chartStackTrace: PropTypes.string,
-  queryResponse: PropTypes.object,
+  queriesResponse: PropTypes.arrayOf(PropTypes.object),
   triggerQuery: PropTypes.bool,
   refreshOverlayVisible: PropTypes.bool,
   errorMessage: PropTypes.node,
@@ -150,14 +150,8 @@ class Chart extends React.PureComponent {
     });
   }
 
-  renderErrorMessage() {
-    const {
-      chartAlert,
-      chartStackTrace,
-      dashboardId,
-      owners,
-      queryResponse,
-    } = this.props;
+  renderErrorMessage(queryResponse) {
+    const { chartAlert, chartStackTrace, dashboardId, owners } = this.props;
 
     const error = queryResponse?.errors?.[0];
     if (error) {
@@ -187,14 +181,14 @@ class Chart extends React.PureComponent {
       errorMessage,
       onQuery,
       refreshOverlayVisible,
+      queriesResponse = [],
     } = this.props;
 
     const isLoading = chartStatus === 'loading';
-
     const isFaded = refreshOverlayVisible && !errorMessage;
     this.renderContainerStartTime = Logger.getTimestamp();
     if (chartStatus === 'failed') {
-      return this.renderErrorMessage();
+      return queriesResponse.map(item => this.renderErrorMessage(item));
     }
     if (errorMessage) {
       return (
diff --git a/superset-frontend/src/chart/ChartRenderer.jsx 
b/superset-frontend/src/chart/ChartRenderer.jsx
index dce5e75..81699ca 100644
--- a/superset-frontend/src/chart/ChartRenderer.jsx
+++ b/superset-frontend/src/chart/ChartRenderer.jsx
@@ -37,7 +37,7 @@ const propTypes = {
   // state
   chartAlert: PropTypes.string,
   chartStatus: PropTypes.string,
-  queryResponse: PropTypes.object,
+  queriesResponse: PropTypes.arrayOf(PropTypes.object),
   triggerQuery: PropTypes.bool,
   refreshOverlayVisible: PropTypes.bool,
   // dashboard callbacks
@@ -78,14 +78,14 @@ class ChartRenderer extends React.Component {
 
   shouldComponentUpdate(nextProps) {
     const resultsReady =
-      nextProps.queryResponse &&
+      nextProps.queriesResponse &&
       ['success', 'rendered'].indexOf(nextProps.chartStatus) > -1 &&
-      !nextProps.queryResponse.error &&
+      !nextProps.queriesResponse?.[0]?.error &&
       !nextProps.refreshOverlayVisible;
 
     if (resultsReady) {
       this.hasQueryResponseChange =
-        nextProps.queryResponse !== this.props.queryResponse;
+        nextProps.queriesResponse !== this.props.queriesResponse;
       return (
         this.hasQueryResponseChange ||
         nextProps.annotationData !== this.props.annotationData ||
@@ -179,7 +179,7 @@ class ChartRenderer extends React.Component {
       datasource,
       initialValues,
       formData,
-      queryResponse,
+      queriesResponse,
     } = this.props;
 
     // It's bad practice to use unprefixed `vizType` as classnames for chart
@@ -218,7 +218,8 @@ class ChartRenderer extends React.Component {
         initialValues={initialValues}
         formData={formData}
         hooks={this.hooks}
-        queryData={queryResponse}
+        queryData={queriesResponse?.[0]} // deprecated
+        queriesData={queriesResponse}
         onRenderSuccess={this.handleRenderSuccess}
         onRenderFailure={this.handleRenderFailure}
       />
diff --git a/superset-frontend/src/chart/chartAction.js 
b/superset-frontend/src/chart/chartAction.js
index 0198b33..1e0c332 100644
--- a/superset-frontend/src/chart/chartAction.js
+++ b/superset-frontend/src/chart/chartAction.js
@@ -52,8 +52,8 @@ export function chartUpdateStarted(queryController, 
latestQueryFormData, key) {
 }
 
 export const CHART_UPDATE_SUCCEEDED = 'CHART_UPDATE_SUCCEEDED';
-export function chartUpdateSucceeded(queryResponse, key) {
-  return { type: CHART_UPDATE_SUCCEEDED, queryResponse, key };
+export function chartUpdateSucceeded(queriesResponse, key) {
+  return { type: CHART_UPDATE_SUCCEEDED, queriesResponse, key };
 }
 
 export const CHART_UPDATE_STOPPED = 'CHART_UPDATE_STOPPED';
@@ -62,8 +62,8 @@ export function chartUpdateStopped(key) {
 }
 
 export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED';
-export function chartUpdateFailed(queryResponse, key) {
-  return { type: CHART_UPDATE_FAILED, queryResponse, key };
+export function chartUpdateFailed(queriesResponse, key) {
+  return { type: CHART_UPDATE_FAILED, queriesResponse, key };
 }
 
 export const CHART_UPDATE_QUEUED = 'CHART_UPDATE_QUEUED';
@@ -361,38 +361,35 @@ export function exploreJSON(
 
     const chartDataRequestCaught = chartDataRequest
       .then(response => {
+        const queriesResponse = response.result;
         if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) {
           // deal with getChartDataRequest transforming the response data
           const result = 'result' in response ? response.result[0] : response;
           return dispatch(chartUpdateQueued(result, key));
         }
 
-        // new API returns an object with an array of restults
-        // problem: response holds a list of results, when before we were just 
getting one result.
-        // How to make the entire app compatible with multiple results?
-        // For now just use the first result.
-        const result = response.result[0];
-
-        dispatch(
-          logEvent(LOG_ACTIONS_LOAD_CHART, {
-            slice_id: key,
-            applied_filters: result.applied_filters,
-            is_cached: result.is_cached,
-            force_refresh: force,
-            row_count: result.rowcount,
-            datasource: formData.datasource,
-            start_offset: logStart,
-            ts: new Date().getTime(),
-            duration: Logger.getTimestamp() - logStart,
-            has_extra_filters:
-              formData.extra_filters && formData.extra_filters.length > 0,
-            viz_type: formData.viz_type,
-            data_age: result.is_cached
-              ? moment(new Date()).diff(moment.utc(result.cached_dttm))
-              : null,
-          }),
+        queriesResponse.forEach(resultItem =>
+          dispatch(
+            logEvent(LOG_ACTIONS_LOAD_CHART, {
+              slice_id: key,
+              applied_filters: resultItem.applied_filters,
+              is_cached: resultItem.is_cached,
+              force_refresh: force,
+              row_count: resultItem.rowcount,
+              datasource: formData.datasource,
+              start_offset: logStart,
+              ts: new Date().getTime(),
+              duration: Logger.getTimestamp() - logStart,
+              has_extra_filters:
+                formData.extra_filters && formData.extra_filters.length > 0,
+              viz_type: formData.viz_type,
+              data_age: resultItem.is_cached
+                ? moment(new Date()).diff(moment.utc(resultItem.cached_dttm))
+                : null,
+            }),
+          ),
         );
-        return dispatch(chartUpdateSucceeded(result, key));
+        return dispatch(chartUpdateSucceeded(queriesResponse, key));
       })
       .catch(response => {
         const appendErrorLog = (errorDetails, isCached) => {
@@ -419,7 +416,7 @@ export function exploreJSON(
           } else {
             appendErrorLog(parsedResponse.error, parsedResponse.is_cached);
           }
-          return dispatch(chartUpdateFailed(parsedResponse, key));
+          return dispatch(chartUpdateFailed([parsedResponse], key));
         });
       });
 
diff --git a/superset-frontend/src/chart/chartReducer.js 
b/superset-frontend/src/chart/chartReducer.js
index fc28e99..923caf1 100644
--- a/superset-frontend/src/chart/chartReducer.js
+++ b/superset-frontend/src/chart/chartReducer.js
@@ -30,7 +30,7 @@ export const chart = {
   chartUpdateStartTime: 0,
   latestQueryFormData: {},
   queryController: null,
-  queryResponse: null,
+  queriesResponse: null,
   triggerQuery: true,
   lastRendered: 0,
 };
@@ -47,8 +47,8 @@ export default function chartReducer(charts = {}, action) {
       return {
         ...state,
         chartStatus: 'success',
-        queryResponse: action.queryResponse,
         chartAlert: null,
+        queriesResponse: action.queriesResponse,
         chartUpdateEndTime: now(),
       };
     },
@@ -97,13 +97,13 @@ export default function chartReducer(charts = {}, action) {
       return {
         ...state,
         chartStatus: 'failed',
-        chartAlert: action.queryResponse
-          ? action.queryResponse.error
+        chartAlert: action.queriesResponse
+          ? action.queriesResponse?.[0]?.error
           : t('Network error.'),
         chartUpdateEndTime: now(),
-        queryResponse: action.queryResponse,
-        chartStackTrace: action.queryResponse
-          ? action.queryResponse.stacktrace
+        queriesResponse: action.queriesResponse,
+        chartStackTrace: action.queriesResponse
+          ? action.queriesResponse?.[0]?.stacktrace
           : null,
       };
     },
diff --git 
a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts 
b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts
index 9cdd43a..6aaa0d9 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts
@@ -132,12 +132,12 @@ export const selectIndicatorsForChart = (
   // for now we only need to know which columns are compatible/incompatible,
   // so grab the columns from the applied/rejected filters
   const appliedColumns: Set<string> = new Set(
-    (chart?.queryResponse?.applied_filters || []).map(
+    (chart?.queriesResponse?.[0]?.applied_filters || []).map(
       (filter: any) => filter.column,
     ),
   );
   const rejectedColumns: Set<string> = new Set(
-    (chart?.queryResponse?.rejected_filters || []).map(
+    (chart?.queriesResponse?.[0]?.rejected_filters || []).map(
       (filter: any) => filter.column,
     ),
   );
diff --git a/superset-frontend/src/dashboard/components/SliceHeader.jsx 
b/superset-frontend/src/dashboard/components/SliceHeader.jsx
index 1f62d83..605ba8e 100644
--- a/superset-frontend/src/dashboard/components/SliceHeader.jsx
+++ b/superset-frontend/src/dashboard/components/SliceHeader.jsx
@@ -29,8 +29,8 @@ const propTypes = {
   innerRef: PropTypes.func,
   slice: PropTypes.object.isRequired,
   isExpanded: PropTypes.bool,
-  isCached: PropTypes.bool,
-  cachedDttm: PropTypes.string,
+  isCached: PropTypes.arrayOf(PropTypes.bool),
+  cachedDttm: PropTypes.arrayOf(PropTypes.string),
   updatedDttm: PropTypes.number,
   updateSliceName: PropTypes.func,
   toggleExpandSlice: PropTypes.func,
@@ -64,8 +64,8 @@ const defaultProps = {
   annotationError: {},
   cachedDttm: null,
   updatedDttm: null,
-  isCached: false,
-  isExpanded: false,
+  isCached: [],
+  isExpanded: [],
   sliceName: '',
   supersetCanExplore: false,
   supersetCanCSV: false,
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx 
b/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx
index 5835199..c88b8f7 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx
@@ -31,9 +31,9 @@ const propTypes = {
   componentId: PropTypes.string.isRequired,
   dashboardId: PropTypes.number.isRequired,
   addDangerToast: PropTypes.func.isRequired,
-  isCached: PropTypes.bool,
+  isCached: PropTypes.arrayOf(PropTypes.bool),
+  cachedDttm: PropTypes.arrayOf(PropTypes.string),
   isExpanded: PropTypes.bool,
-  cachedDttm: PropTypes.string,
   updatedDttm: PropTypes.number,
   supersetCanExplore: PropTypes.bool,
   supersetCanCSV: PropTypes.bool,
@@ -49,9 +49,9 @@ const defaultProps = {
   toggleExpandSlice: () => ({}),
   exploreChart: () => ({}),
   exportCSV: () => ({}),
-  cachedDttm: null,
+  cachedDttm: [],
   updatedDttm: null,
-  isCached: false,
+  isCached: [],
   isExpanded: false,
   supersetCanExplore: false,
   supersetCanCSV: false,
@@ -82,9 +82,14 @@ const VerticalDotsContainer = styled.div`
 `;
 
 const RefreshTooltip = styled.div`
-  height: ${({ theme }) => theme.gridUnit * 4}px;
+  height: auto;
   margin: ${({ theme }) => theme.gridUnit}px 0;
   color: ${({ theme }) => theme.colors.grayscale.base};
+  line-height: ${({ theme }) => theme.typography.sizes.m * 1.5}px;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  justify-content: flex-start;
 `;
 
 const SCREENSHOT_NODE_SELECTOR = '.dashboard-component-chart-holder';
@@ -171,13 +176,26 @@ class SliceHeaderControls extends React.PureComponent {
       addDangerToast,
       isFullSize,
     } = this.props;
-    const cachedWhen = moment.utc(cachedDttm).fromNow();
+    const cachedWhen = cachedDttm.map(itemCachedDttm =>
+      moment.utc(itemCachedDttm).fromNow(),
+    );
     const updatedWhen = updatedDttm ? moment.utc(updatedDttm).fromNow() : '';
-    const refreshTooltip = isCached
-      ? t('Cached %s', cachedWhen)
-      : (updatedWhen && t('Fetched %s', updatedWhen)) || '';
+    const getCachedTitle = itemCached => {
+      return itemCached
+        ? t('Cached %s', cachedWhen)
+        : updatedWhen && t('Fetched %s', updatedWhen);
+    };
+    const refreshTooltipData = isCached.map(getCachedTitle) || '';
+    // If all queries have same cache time we can unit them to one
+    let refreshTooltip = [...new Set(refreshTooltipData)];
+    refreshTooltip = refreshTooltip.map((item, index) => (
+      <div>
+        {refreshTooltip.length > 1
+          ? `${t('Query')} ${index + 1}: ${item}`
+          : item}
+      </div>
+    ));
     const resizeLabel = isFullSize ? t('Minimize Chart') : t('Maximize Chart');
-
     const menu = (
       <Menu
         onClick={this.handleMenuClick}
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
index 7e4d809..a7d2b83 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
@@ -266,10 +266,13 @@ export default class Chart extends React.Component {
       return <MissingChart height={this.getChartHeight()} />;
     }
 
-    const { queryResponse, chartUpdateEndTime, chartStatus } = chart;
+    const { queriesResponse, chartUpdateEndTime, chartStatus } = chart;
     const isLoading = chartStatus === 'loading';
-    const isCached = queryResponse && queryResponse.is_cached;
-    const cachedDttm = queryResponse && queryResponse.cached_dttm;
+    // eslint-disable-next-line camelcase
+    const isCached = queriesResponse?.map(({ is_cached }) => is_cached) || [];
+    const cachedDttm =
+      // eslint-disable-next-line camelcase
+      queriesResponse?.map(({ cached_dttm }) => cached_dttm) || [];
     const isOverflowable = OVERFLOWABLE_VIZ_TYPES.has(slice.viz_type);
     const initialValues = isFilterBox(id)
       ? getFilterValuesByFilterId({
@@ -277,7 +280,6 @@ export default class Chart extends React.Component {
           filterId: id,
         })
       : {};
-
     return (
       <div className="chart-slice">
         <SliceHeader
@@ -352,7 +354,7 @@ export default class Chart extends React.Component {
             dashboardId={dashboardId}
             initialValues={initialValues}
             formData={formData}
-            queryResponse={chart.queryResponse}
+            queriesResponse={chart.queriesResponse}
             timeout={timeout}
             triggerQuery={chart.triggerQuery}
             vizType={slice.viz_type}
diff --git a/superset-frontend/src/dashboard/util/propShapes.jsx 
b/superset-frontend/src/dashboard/util/propShapes.jsx
index b8c2b09..78bcf3f 100644
--- a/superset-frontend/src/dashboard/util/propShapes.jsx
+++ b/superset-frontend/src/dashboard/util/propShapes.jsx
@@ -50,7 +50,7 @@ export const chartPropShape = PropTypes.shape({
   chartUpdateStartTime: PropTypes.number,
   latestQueryFormData: PropTypes.object,
   queryController: PropTypes.shape({ abort: PropTypes.func }),
-  queryResponse: PropTypes.object,
+  queriesResponse: PropTypes.arrayOf(PropTypes.object),
   triggerQuery: PropTypes.bool,
   lastRendered: PropTypes.number,
 });
diff --git a/superset-frontend/src/explore/components/DisplayQueryButton.jsx 
b/superset-frontend/src/explore/components/DisplayQueryButton.jsx
index e292839..e19beb6 100644
--- a/superset-frontend/src/explore/components/DisplayQueryButton.jsx
+++ b/superset-frontend/src/explore/components/DisplayQueryButton.jsx
@@ -47,7 +47,6 @@ SyntaxHighlighter.registerLanguage('json', jsonSyntax);
 const propTypes = {
   onOpenPropertiesModal: PropTypes.func,
   onOpenInEditor: PropTypes.func,
-  queryResponse: PropTypes.object,
   chartStatus: PropTypes.string,
   chartHeight: PropTypes.string.isRequired,
   latestQueryFormData: PropTypes.object.isRequired,
diff --git a/superset-frontend/src/explore/components/ExploreActionButtons.jsx 
b/superset-frontend/src/explore/components/ExploreActionButtons.jsx
index c96fbb8..d485696 100644
--- a/superset-frontend/src/explore/components/ExploreActionButtons.jsx
+++ b/superset-frontend/src/explore/components/ExploreActionButtons.jsx
@@ -33,7 +33,7 @@ const propTypes = {
   chartStatus: PropTypes.string,
   chartHeight: PropTypes.string.isRequired,
   latestQueryFormData: PropTypes.object,
-  queryResponse: PropTypes.object,
+  queriesResponse: PropTypes.arrayOf(PropTypes.object),
   slice: PropTypes.object,
 };
 
@@ -43,7 +43,7 @@ export default function ExploreActionButtons({
   chartHeight,
   chartStatus,
   latestQueryFormData,
-  queryResponse,
+  queriesResponse,
   slice,
 }) {
   const exportToCSVClasses = cx('btn btn-default btn-sm', {
@@ -106,7 +106,7 @@ export default function ExploreActionButtons({
       )}
       <ConnectedDisplayQueryButton
         chartHeight={chartHeight}
-        queryResponse={queryResponse}
+        queryResponse={queriesResponse?.[0]}
         latestQueryFormData={latestQueryFormData}
         chartStatus={chartStatus}
         onOpenInEditor={actions.redirectSQLLab}
diff --git a/superset-frontend/src/explore/components/ExploreChartHeader.jsx 
b/superset-frontend/src/explore/components/ExploreChartHeader.jsx
index 88a54b3..9c1df9f 100644
--- a/superset-frontend/src/explore/components/ExploreChartHeader.jsx
+++ b/superset-frontend/src/explore/components/ExploreChartHeader.jsx
@@ -130,8 +130,10 @@ export class ExploreChartHeader extends 
React.PureComponent {
       chartUpdateEndTime,
       chartUpdateStartTime,
       latestQueryFormData,
-      queryResponse,
+      queriesResponse,
     } = this.props.chart;
+    // TODO: when will get appropriate design for multi queries use all 
results and not first only
+    const queryResponse = queriesResponse?.[0];
     const chartFinished = ['failed', 'rendered', 'success'].includes(
       this.props.chart.chartStatus,
     );
diff --git a/superset-frontend/src/explore/components/ExploreChartPanel.jsx 
b/superset-frontend/src/explore/components/ExploreChartPanel.jsx
index bda8498..0c608ea 100644
--- a/superset-frontend/src/explore/components/ExploreChartPanel.jsx
+++ b/superset-frontend/src/explore/components/ExploreChartPanel.jsx
@@ -189,7 +189,7 @@ const ExploreChartPanel = props => {
               formData={props.form_data}
               onQuery={props.onQuery}
               owners={props?.slice?.owners}
-              queryResponse={chart.queryResponse}
+              queriesResponse={chart.queriesResponse}
               refreshOverlayVisible={props.refreshOverlayVisible}
               setControlValue={props.actions.setControlValue}
               timeout={props.timeout}
diff --git a/superset-frontend/src/explore/reducers/getInitialState.js 
b/superset-frontend/src/explore/reducers/getInitialState.js
index 53dad95..0774340 100644
--- a/superset-frontend/src/explore/reducers/getInitialState.js
+++ b/superset-frontend/src/explore/reducers/getInitialState.js
@@ -72,7 +72,7 @@ export default function getInitialState(bootstrapData) {
         latestQueryFormData: getFormDataFromControls(controls),
         sliceFormData,
         queryController: null,
-        queryResponse: null,
+        queriesResponse: null,
         triggerQuery: false,
         lastRendered: 0,
       },
diff --git a/superset-frontend/src/middleware/asyncEvent.ts 
b/superset-frontend/src/middleware/asyncEvent.ts
index 32d4010..b7a4912 100644
--- a/superset-frontend/src/middleware/asyncEvent.ts
+++ b/superset-frontend/src/middleware/asyncEvent.ts
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Middleware, MiddlewareAPI, Dispatch } from 'redux';
+import { Dispatch, Middleware, MiddlewareAPI } from 'redux';
 import { makeApi, SupersetClient } from '@superset-ui/core';
 import { SupersetError } from 'src/components/ErrorMessage/types';
-import { isFeatureEnabled, FeatureFlag } from '../featureFlags';
+import { FeatureFlag, isFeatureEnabled } from '../featureFlags';
 import {
   getClientErrorObject,
   parseErrorJson,
@@ -100,7 +100,7 @@ const initAsyncEvents = (options: AsyncEventOptions) => {
         const { json } = await SupersetClient.get({
           endpoint: asyncEvent.result_url,
         });
-        data = 'result' in json ? json.result[0] : json;
+        data = 'result' in json ? json.result : json;
       } catch (response) {
         status = 'error';
         data = await getClientErrorObject(response);
@@ -152,7 +152,7 @@ const initAsyncEvents = (options: AsyncEventOptions) => {
                   break;
                 case JOB_STATUS.ERROR:
                   store.dispatch(
-                    errorAction(componentId, parseErrorJson(asyncEvent)),
+                    errorAction(componentId, [parseErrorJson(asyncEvent)]),
                   );
                   break;
                 default:
@@ -164,10 +164,13 @@ const initAsyncEvents = (options: AsyncEventOptions) => {
 
             const fetchResults = await Promise.all(fetchDataEvents);
             fetchResults.forEach(result => {
+              const data = Array.isArray(result.data)
+                ? result.data
+                : [result.data];
               if (result.status === 'success') {
-                store.dispatch(successAction(result.componentId, result.data));
+                store.dispatch(successAction(result.componentId, data));
               } else {
-                store.dispatch(errorAction(result.componentId, result.data));
+                store.dispatch(errorAction(result.componentId, data));
               }
             });
           }

Reply via email to