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

feat(SIP-39): Async query support for charts #11499

Merged
merged 50 commits into from
Dec 11, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
67f7d5f
Generate JWT in Flask app
robdiciuccio Oct 21, 2020
1e8c039
Refactor chart data API query logic, add JWT validation and async worker
robdiciuccio Oct 22, 2020
a50750b
Add redis stream implementation, refactoring
robdiciuccio Oct 27, 2020
64fbfae
Add chart data cache endpoint, refactor QueryContext caching
robdiciuccio Oct 30, 2020
219ce77
Merge branch master
robdiciuccio Nov 10, 2020
e377b31
Typing, linting, refactoring
robdiciuccio Nov 10, 2020
f7ac5b6
pytest fixes and openapi schema update
robdiciuccio Nov 11, 2020
3a16283
Merge branch master
robdiciuccio Nov 18, 2020
6805180
Enforce caching be configured for async query init
robdiciuccio Nov 19, 2020
d5eef4f
Async query processing for explore_json endpoint
robdiciuccio Nov 19, 2020
467c6bb
Add /api/v1/async_event endpoint
robdiciuccio Nov 20, 2020
f867cd5
Async frontend for dashboards [WIP]
robdiciuccio Nov 24, 2020
8111ea6
Chart async error message support, refactoring
robdiciuccio Nov 30, 2020
3b41f16
Abstract asyncEvent middleware
robdiciuccio Nov 30, 2020
49b6b52
Async chart loading for Explore
robdiciuccio Nov 30, 2020
0e5c09e
Merge branch master
robdiciuccio Nov 30, 2020
e2dc30e
Pylint fixes
robdiciuccio Nov 30, 2020
276c84e
asyncEvent middleware -> TypeScript, JS linting
robdiciuccio Dec 1, 2020
4566c01
Chart data API: enforce forced_cache, add tests
robdiciuccio Dec 2, 2020
9eba0c4
Add tests for explore_json endpoints
robdiciuccio Dec 3, 2020
d2e4529
Add test for chart data cache enpoint (no login)
robdiciuccio Dec 3, 2020
0bd7d67
Consolidate set_and_log_cache and add STORE_CACHE_KEYS_IN_METADATA_DB…
robdiciuccio Dec 3, 2020
4441459
Add tests for tasks/async_queries and address PR comments
robdiciuccio Dec 3, 2020
5896799
Bypass non-JSON result formats for async queries
robdiciuccio Dec 3, 2020
5999bf3
Add tests for redux middleware
robdiciuccio Dec 4, 2020
17215a3
Merge branch master
robdiciuccio Dec 4, 2020
27e4548
Remove debug statement
robdiciuccio Dec 4, 2020
119cae7
Skip force_cached if no queryObj
robdiciuccio Dec 4, 2020
9666408
SunburstViz: don't modify self.form_data
robdiciuccio Dec 4, 2020
d2ef464
Merge branch 'rd/async-queries-mvp' of github.com:preset-io/incubator…
robdiciuccio Dec 4, 2020
a8607c0
Merge branch master
robdiciuccio Dec 7, 2020
e40eb45
Fix failing annotation test
robdiciuccio Dec 7, 2020
f491789
Resolve merge/lint issues
robdiciuccio Dec 7, 2020
f01740e
Reduce polling delay
robdiciuccio Dec 8, 2020
9e02017
Merge branch 'master' into rd/async-queries-mvp
robdiciuccio Dec 8, 2020
838c526
Fix new getClientErrorObject reference
robdiciuccio Dec 8, 2020
f0de265
Fix flakey unit tests
robdiciuccio Dec 8, 2020
066504f
/api/v1/async_event: increment redis stream ID, add tests
robdiciuccio Dec 9, 2020
d6c8a1d
PR feedback: refactoring, configuration
robdiciuccio Dec 9, 2020
fc5753a
Merge branch master
robdiciuccio Dec 9, 2020
088a49c
Fixup: remove debugging
robdiciuccio Dec 9, 2020
c9b871e
Fix typescript errors due to redux upgrade
robdiciuccio Dec 10, 2020
89925a5
Update UPDATING.md
robdiciuccio Dec 10, 2020
0ad7234
Fix failing py tests
robdiciuccio Dec 10, 2020
c72b4c6
asyncEvent_spec.js -> asyncEvent_spec.ts
robdiciuccio Dec 10, 2020
1fb7489
Refactor flakey Python 3.7 mock assertions
robdiciuccio Dec 10, 2020
024da76
Fix another shared state issue in Py tests
robdiciuccio Dec 10, 2020
887754b
Use 'sub' claim in JWT for user_id
robdiciuccio Dec 11, 2020
601ec51
Refactor async middleware config
robdiciuccio Dec 11, 2020
df673cf
Fixup: restore FeatureFlag boolean type
robdiciuccio Dec 11, 2020
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
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ combine_as_imports = true
include_trailing_comma = true
line_length = 88
known_first_party = superset
known_third_party =alembic,apispec,backoff,bleach,cachelib,celery,click,colorama,contextlib2,croniter,cryptography,dateutil,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,freezegun,geohash,geopy,humanize,isodate,jinja2,markdown,markupsafe,marshmallow,msgpack,numpy,pandas,parameterized,parsedatetime,pathlib2,polyline,prison,pyarrow,pyhive,pytest,pytz,retry,selenium,setuptools,simplejson,slack,sqlalchemy,sqlalchemy_utils,sqlparse,werkzeug,wtforms,wtforms_json,yaml
known_third_party =alembic,apispec,backoff,bleach,cachelib,celery,click,colorama,contextlib2,croniter,cryptography,dateutil,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,freezegun,geohash,geopy,humanize,isodate,jinja2,jwt,markdown,markupsafe,marshmallow,msgpack,numpy,pandas,parameterized,parsedatetime,pathlib2,polyline,prison,pyarrow,pyhive,pytest,pytz,redis,retry,selenium,setuptools,simplejson,slack,sqlalchemy,sqlalchemy_utils,sqlparse,werkzeug,wtforms,wtforms_json,yaml
multi_line_output = 3
order_by_type = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import { ErrorTypeEnum } from 'src/components/ErrorMessage/types';
import getClientErrorObject from 'src/utils/getClientErrorObject';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';

describe('getClientErrorObject()', () => {
it('Returns a Promise', () => {
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/SqlLab/actions/sqlLab.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
addSuccessToast as addSuccessToastAction,
addWarningToast as addWarningToastAction,
} from '../../messageToasts/actions/index';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';
import COMMON_ERR_MESSAGES from '../../utils/errorMessages';

export const RESET_STATE = 'RESET_STATE';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import Button from 'src/components/Button';
import CopyToClipboard from '../../components/CopyToClipboard';
import { storeQuery } from '../../utils/common';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';
import withToasts from '../../messageToasts/enhancers/withToasts';

const propTypes = {
Expand Down
2 changes: 2 additions & 0 deletions superset-frontend/src/chart/Chart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ class Chart extends React.PureComponent {
queryResponse,
} = this.props;

console.log('**** Chart renderError', queryResponse);

robdiciuccio marked this conversation as resolved.
Show resolved Hide resolved
const error = queryResponse?.errors?.[0];
if (error) {
const extra = error.extra || {};
Expand Down
13 changes: 12 additions & 1 deletion superset-frontend/src/chart/chartAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
import { addDangerToast } from '../messageToasts/actions';
import { logEvent } from '../logger/actions';
import { Logger, LOG_ACTIONS_LOAD_CHART } from '../logger/LogUtils';
import getClientErrorObject from '../utils/getClientErrorObject';
import { getClientErrorObject } from '../utils/getClientErrorObject';
import { allowCrossDomain as domainShardingEnabled } from '../utils/hostNamesConfig';

export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED';
Expand Down Expand Up @@ -66,6 +66,11 @@ export function chartUpdateFailed(queryResponse, key) {
return { type: CHART_UPDATE_FAILED, queryResponse, key };
}

export const CHART_UPDATE_QUEUED = 'CHART_UPDATE_QUEUED';
export function chartUpdateQueued(asyncJobMeta, key) {
return { type: CHART_UPDATE_QUEUED, asyncJobMeta, key };
}

export const CHART_RENDERING_FAILED = 'CHART_RENDERING_FAILED';
export function chartRenderingFailed(error, key, stackTrace) {
return { type: CHART_RENDERING_FAILED, error, key, stackTrace };
Expand Down Expand Up @@ -356,6 +361,12 @@ export function exploreJSON(

const chartDataRequestCaught = chartDataRequest
.then(response => {
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?
Expand Down
8 changes: 8 additions & 0 deletions superset-frontend/src/chart/chartReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ export default function chartReducer(charts = {}, action) {
chartUpdateEndTime: now(),
};
},
[actions.CHART_UPDATE_QUEUED](state) {
return {
...state,
asyncJobId: action.asyncJobMeta.job_id,
chartStatus: 'loading',
chartUpdateEndTime: now(),
};
},
[actions.CHART_RENDERING_SUCCEEDED](state) {
return { ...state, chartStatus: 'rendered', chartUpdateEndTime: now() };
},
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/components/AsyncSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
// TODO: refactor this with `import { AsyncSelect } from src/components/Select`
import { Select } from 'src/components/Select';
import { t, SupersetClient } from '@superset-ui/core';
import getClientErrorObject from '../utils/getClientErrorObject';
import { getClientErrorObject } from '../utils/getClientErrorObject';

const propTypes = {
dataEndpoint: PropTypes.string.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/dashboard/actions/dashboardState.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
updateDirectPathToFilter,
} from './dashboardFilters';
import { applyDefaultFormData } from '../../explore/store';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';
import { SAVE_TYPE_OVERWRITE } from '../util/constants';
import {
addSuccessToast,
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/dashboard/actions/datasources.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import { SupersetClient } from '@superset-ui/core';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';

export const SET_DATASOURCE = 'SET_DATASOURCE';
export function setDatasource(datasource, key) {
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/dashboard/actions/sliceEntities.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import rison from 'rison';

import { addDangerToast } from 'src/messageToasts/actions';
import { getDatasourceParameter } from 'src/modules/utils';
import getClientErrorObject from 'src/utils/getClientErrorObject';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';

export const SET_ALL_SLICES = 'SET_ALL_SLICES';
export function setAllSlices(slices) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import FormLabel from 'src/components/FormLabel';
import { JsonEditor } from 'src/components/AsyncAceEditor';

import ColorSchemeControlWrapper from 'src/dashboard/components/ColorSchemeControlWrapper';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';
import withToasts from '../../messageToasts/enhancers/withToasts';
import '../stylesheets/buttons.less';

Expand Down
17 changes: 16 additions & 1 deletion superset-frontend/src/dashboard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { filter } from 'lodash';
import React from 'react';
import ReactDOM from 'react-dom';
import thunk from 'redux-thunk';
Expand All @@ -24,7 +25,9 @@ import { initFeatureFlags } from 'src/featureFlags';
import { initEnhancer } from '../reduxUtils';
import getInitialState from './reducers/getInitialState';
import rootReducer from './reducers/index';
import initAsyncEvents from '../middleware/asyncEvent';
import logger from '../middleware/loggerMiddleware';
import * as actions from '../chart/chartAction';

import App from './App';

Expand All @@ -33,10 +36,22 @@ const bootstrapData = JSON.parse(appContainer.getAttribute('data-bootstrap'));
initFeatureFlags(bootstrapData.common.feature_flags);
const initState = getInitialState(bootstrapData);

const asyncEventMiddleware = initAsyncEvents({
getPendingComponents: state =>
filter(state.charts, { chartStatus: 'loading' }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

  getPendingComponents: ({ charts }) => charts.filter(x => x.chartStatus === 'loading')

One less dependency and more straightforward code

successAction: (componentId, componentData) =>
actions.chartUpdateSucceeded(componentData, componentId),
errorAction: (componentId, response) =>
actions.chartUpdateFailed(response, componentId),
});

const store = createStore(
rootReducer,
initState,
compose(applyMiddleware(thunk, logger), initEnhancer(false)),
compose(
applyMiddleware(thunk, logger, asyncEventMiddleware),
initEnhancer(false),
),
);

ReactDOM.render(<App store={store} />, document.getElementById('app'));
2 changes: 1 addition & 1 deletion superset-frontend/src/datasource/ChangeDatasourceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Alert, FormControl, FormControlProps } from 'react-bootstrap';
import { SupersetClient, t } from '@superset-ui/core';
import TableView from 'src/components/TableView';
import Modal from 'src/common/components/Modal';
import getClientErrorObject from '../utils/getClientErrorObject';
import { getClientErrorObject } from '../utils/getClientErrorObject';
import Loading from '../components/Loading';
import withToasts from '../messageToasts/enhancers/withToasts';

Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/datasource/DatasourceEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import Loading from 'src/components/Loading';
import TableSelector from 'src/components/TableSelector';
import EditableTitle from 'src/components/EditableTitle';

import getClientErrorObject from 'src/utils/getClientErrorObject';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';

import CheckboxControl from 'src/explore/components/controls/CheckboxControl';
import TextControl from 'src/explore/components/controls/TextControl';
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/datasource/DatasourceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import Modal from 'src/common/components/Modal';
import AsyncEsmComponent from 'src/components/AsyncEsmComponent';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';

import getClientErrorObject from 'src/utils/getClientErrorObject';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import withToasts from 'src/messageToasts/enhancers/withToasts';

const DatasourceEditor = AsyncEsmComponent(() => import('./DatasourceEditor'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { styled, t } from '@superset-ui/core';
import { Menu } from 'src/common/components';
import TableView, { EmptyWrapperType } from 'src/components/TableView';
import Button from 'src/components/Button';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';
import CopyToClipboard from '../../components/CopyToClipboard';
import { getChartDataRequest } from '../../chart/chartAction';
import downloadAsImage from '../../utils/downloadAsImage';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import rison from 'rison';
import { t, SupersetClient } from '@superset-ui/core';
import Chart, { Slice } from 'src/types/Chart';
import FormLabel from 'src/components/FormLabel';
import getClientErrorObject from '../../utils/getClientErrorObject';
import { getClientErrorObject } from '../../utils/getClientErrorObject';

type PropertiesModalProps = {
slice: Slice;
Expand Down
17 changes: 16 additions & 1 deletion superset-frontend/src/explore/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { filter } from 'lodash';
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
Expand All @@ -25,6 +26,8 @@ import { initFeatureFlags } from '../featureFlags';
import { initEnhancer } from '../reduxUtils';
import getInitialState from './reducers/getInitialState';
import rootReducer from './reducers/index';
import initAsyncEvents from '../middleware/asyncEvent';
import * as actions from '../chart/chartAction';

import App from './App';

Expand All @@ -35,10 +38,22 @@ const bootstrapData = JSON.parse(
initFeatureFlags(bootstrapData.common.feature_flags);
const initState = getInitialState(bootstrapData);

const asyncEventMiddleware = initAsyncEvents({
getPendingComponents: state =>
filter(state.charts, { chartStatus: 'loading' }),
successAction: (componentId, componentData) =>
actions.chartUpdateSucceeded(componentData, componentId),
errorAction: (componentId, response) =>
actions.chartUpdateFailed(response, componentId),
});

const store = createStore(
rootReducer,
initState,
compose(applyMiddleware(thunk, logger), initEnhancer(false)),
compose(
applyMiddleware(thunk, logger, asyncEventMiddleware),
initEnhancer(false),
),
);

ReactDOM.render(<App store={store} />, document.getElementById('app'));
1 change: 1 addition & 0 deletions superset-frontend/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum FeatureFlag {
ESCAPE_MARKDOWN_HTML = 'ESCAPE_MARKDOWN_HTML',
VERSIONED_EXPORT = 'VERSIONED_EXPORT',
SIP_34_QUERY_SEARCH_UI = 'SIP_34_QUERY_SEARCH_UI',
GLOBAL_ASYNC_QUERIES = 'GLOBAL_ASYNC_QUERIES',
}

export type FeatureFlagMap = {
Expand Down
Loading