This is an automated email from the ASF dual-hosted git repository.
timi 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 fc3b68e [Sqllab] Add offline state to sqllab (#6013)
fc3b68e is described below
commit fc3b68e23433522b3a6d7327cc95199bf01dcf96
Author: timifasubaa <[email protected]>
AuthorDate: Mon Oct 22 18:36:07 2018 -0700
[Sqllab] Add offline state to sqllab (#6013)
* add timeout and refresh for failed backend
* show offline state instead of refreshing
* add southpane tests
---
.../spec/javascripts/sqllab/SouthPane_spec.jsx | 37 ++++++++++++++++++++++
.../javascripts/sqllab/SqlEditorLeftBar_spec.jsx | 11 +++++--
.../javascripts/sqllab/TabbedSqlEditors_spec.jsx | 6 ++++
superset/assets/src/SqlLab/actions.js | 5 +++
.../src/SqlLab/components/QueryAutoRefresh.jsx | 7 +++-
.../assets/src/SqlLab/components/QuerySearch.jsx | 2 +-
.../assets/src/SqlLab/components/SouthPane.jsx | 12 ++++++-
.../src/SqlLab/components/SqlEditorLeftBar.jsx | 17 +++++++---
.../src/SqlLab/components/TabbedSqlEditors.jsx | 4 +++
superset/assets/src/SqlLab/constants.js | 14 ++++----
superset/assets/src/SqlLab/getInitialState.js | 5 +--
superset/assets/src/SqlLab/reducers.js | 3 ++
12 files changed, 106 insertions(+), 17 deletions(-)
diff --git a/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
new file mode 100644
index 0000000..9836583
--- /dev/null
+++ b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import configureStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+
+import { shallow } from 'enzyme';
+
+import { STATUS_OPTIONS } from '../../../src/SqlLab/constants';
+import { initialState } from './fixtures';
+import SouthPane from '../../../src/SqlLab/components/SouthPane';
+
+describe('SouthPane', () => {
+ const middlewares = [thunk];
+ const mockStore = configureStore(middlewares);
+ const store = mockStore(initialState);
+
+ const mockedProps = {
+ editorQueries: [],
+ dataPreviewQueries: [],
+ actions: {},
+ activeSouthPaneTab: '',
+ height: 1,
+ databases: {},
+ offline: false,
+ };
+
+ const getWrapper = () => (
+ shallow(<SouthPane {...mockedProps} />, {
+ context: { store },
+ }).dive());
+
+ let wrapper;
+ it('should render offline when the state is offline', () => {
+ wrapper = getWrapper();
+ wrapper.setProps({ offline: true });
+
expect(wrapper.find('.m-r-3').render().text()).toBe(STATUS_OPTIONS.offline);
+ });
+});
diff --git a/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
b/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
index b233e19..9d3c3f6 100644
--- a/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/SqlEditorLeftBar_spec.jsx
@@ -1,9 +1,11 @@
import React from 'react';
+import configureStore from 'redux-mock-store';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import fetchMock from 'fetch-mock';
+import thunk from 'redux-thunk';
-import { table, defaultQueryEditor, databases, tables } from './fixtures';
+import { table, defaultQueryEditor, databases, initialState, tables } from
'./fixtures';
import SqlEditorLeftBar from '../../../src/SqlLab/components/SqlEditorLeftBar';
import TableElement from '../../../src/SqlLab/components/TableElement';
@@ -21,11 +23,16 @@ describe('SqlEditorLeftBar', () => {
database: {},
height: 0,
};
+ const middlewares = [thunk];
+ const mockStore = configureStore(middlewares);
+ const store = mockStore(initialState);
let wrapper;
beforeEach(() => {
- wrapper = shallow(<SqlEditorLeftBar {...mockedProps} />);
+ wrapper = shallow(<SqlEditorLeftBar {...mockedProps} />, {
+ context: { store },
+ }).dive();
});
it('is valid', () => {
diff --git a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
index 046e2a6..33d1e47 100644
--- a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
@@ -166,4 +166,10 @@ describe('TabbedSqlEditors', () => {
const lastTab = wrapper.find(Tab).last();
expect(lastTab.props().eventKey).toContain('add_tab');
});
+ it('should disable new tab when offline', () => {
+ wrapper = getWrapper();
+ expect(wrapper.find(Tab).last().props().disabled).toBe(false);
+ wrapper.setProps({ offline: true });
+ expect(wrapper.find(Tab).last().props().disabled).toBe(true);
+ });
});
diff --git a/superset/assets/src/SqlLab/actions.js
b/superset/assets/src/SqlLab/actions.js
index 8c9ef2d..91e8486 100644
--- a/superset/assets/src/SqlLab/actions.js
+++ b/superset/assets/src/SqlLab/actions.js
@@ -34,6 +34,7 @@ export const SET_DATABASES = 'SET_DATABASES';
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
export const SET_ACTIVE_SOUTHPANE_TAB = 'SET_ACTIVE_SOUTHPANE_TAB';
export const REFRESH_QUERIES = 'REFRESH_QUERIES';
+export const SET_USER_OFFLINE = 'SET_USER_OFFLINE';
export const RUN_QUERY = 'RUN_QUERY';
export const START_QUERY = 'START_QUERY';
export const STOP_QUERY = 'STOP_QUERY';
@@ -342,6 +343,10 @@ export function refreshQueries(alteredQueries) {
return { type: REFRESH_QUERIES, alteredQueries };
}
+export function setUserOffline(offline) {
+ return { type: SET_USER_OFFLINE, offline };
+}
+
export function persistEditorHeight(queryEditor, currentHeight) {
return { type: QUERY_EDITOR_PERSIST_HEIGHT, queryEditor, currentHeight };
}
diff --git a/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
b/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
index 0b09364..ea6e780 100644
--- a/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
+++ b/superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
@@ -9,6 +9,7 @@ import * as Actions from '../actions';
const QUERY_UPDATE_FREQ = 2000;
const QUERY_UPDATE_BUFFER_MS = 5000;
const MAX_QUERY_AGE_TO_POLL = 21600000;
+const QUERY_TIMEOUT_LIMIT = 7000;
class QueryAutoRefresh extends React.PureComponent {
componentWillMount() {
@@ -44,11 +45,15 @@ class QueryAutoRefresh extends React.PureComponent {
if (this.shouldCheckForQueries()) {
SupersetClient.get({
endpoint: `/superset/queries/${this.props.queriesLastUpdate -
QUERY_UPDATE_BUFFER_MS}`,
+ timeout: QUERY_TIMEOUT_LIMIT,
}).then(({ json }) => {
if (Object.keys(json).length > 0) {
this.props.actions.refreshQueries(json);
}
- });
+ this.props.actions.setUserOffline(false);
+ }).catch(() => {
+ this.props.actions.setUserOffline(true);
+ });
}
}
render() {
diff --git a/superset/assets/src/SqlLab/components/QuerySearch.jsx
b/superset/assets/src/SqlLab/components/QuerySearch.jsx
index a3d9ddf..9e92029 100644
--- a/superset/assets/src/SqlLab/components/QuerySearch.jsx
+++ b/superset/assets/src/SqlLab/components/QuerySearch.jsx
@@ -227,7 +227,7 @@ class QuerySearch extends React.PureComponent {
<Select
name="select-status"
placeholder={t('[Query Status]')}
- options={STATUS_OPTIONS.map(s => ({ value: s, label: s }))}
+ options={Object.keys(STATUS_OPTIONS).map(s => ({ value: s,
label: s }))}
value={this.state.status}
isLoading={false}
autosize={false}
diff --git a/superset/assets/src/SqlLab/components/SouthPane.jsx
b/superset/assets/src/SqlLab/components/SouthPane.jsx
index 65d17f7..ce81c8d 100644
--- a/superset/assets/src/SqlLab/components/SouthPane.jsx
+++ b/superset/assets/src/SqlLab/components/SouthPane.jsx
@@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import shortid from 'shortid';
-import { Alert, Tab, Tabs } from 'react-bootstrap';
+import { Alert, Label, Tab, Tabs } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
+import { STATUS_OPTIONS, STATE_BSSTYLE_MAP } from '../constants';
import { t } from '../../locales';
/*
@@ -21,10 +22,12 @@ const propTypes = {
activeSouthPaneTab: PropTypes.string,
height: PropTypes.number,
databases: PropTypes.object.isRequired,
+ offline: PropTypes.bool,
};
const defaultProps = {
activeSouthPaneTab: 'Results',
+ offline: false,
};
class SouthPane extends React.PureComponent {
@@ -32,6 +35,12 @@ class SouthPane extends React.PureComponent {
this.props.actions.setActiveSouthPaneTab(id);
}
render() {
+ if (this.props.offline) {
+ return (
+ <Label className="m-r-3"
bsStyle={STATE_BSSTYLE_MAP[STATUS_OPTIONS.offline]}>
+ { STATUS_OPTIONS.offline }
+ </Label>);
+ }
const innerTabHeight = this.props.height - 55;
let latestQuery;
const props = this.props;
@@ -103,6 +112,7 @@ function mapStateToProps({ sqlLab }) {
return {
activeSouthPaneTab: sqlLab.activeSouthPaneTab,
databases: sqlLab.databases,
+ offline: sqlLab.offline,
};
}
diff --git a/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
b/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
index f17f810..12530eb 100644
--- a/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
+++ b/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ControlLabel, Button } from 'react-bootstrap';
+import { connect } from 'react-redux';
import Select from 'react-virtualized-select';
import createFilterOptions from 'react-select-fast-filter-options';
import { SupersetClient } from '@superset-ui/core';
@@ -16,11 +17,13 @@ const propTypes = {
tables: PropTypes.array,
actions: PropTypes.object,
database: PropTypes.object,
+ offline: PropTypes.bool,
};
const defaultProps = {
tables: [],
actions: {},
+ offline: false,
};
class SqlEditorLeftBar extends React.PureComponent {
@@ -50,7 +53,7 @@ class SqlEditorLeftBar extends React.PureComponent {
}
getTableNamesBySubStr(input) {
- if (!this.props.queryEditor.dbId || !input) {
+ if (this.props.offline || !this.props.queryEditor.dbId || !input) {
return Promise.resolve({ options: [] });
}
@@ -77,7 +80,7 @@ class SqlEditorLeftBar extends React.PureComponent {
fetchTables(dbId, schema, force, substr) {
// This can be large so it shouldn't be put in the Redux store
const forceRefresh = force || false;
- if (dbId && schema) {
+ if (!this.props.offline && dbId && schema) {
this.setState(() => ({ tableLoading: true, tableOptions: [] }));
const endpoint =
`/superset/tables/${dbId}/${schema}/${substr}/${forceRefresh}/`;
@@ -130,7 +133,7 @@ class SqlEditorLeftBar extends React.PureComponent {
fetchSchemas(dbId, force) {
const actualDbId = dbId || this.props.queryEditor.dbId;
const forceRefresh = force || false;
- if (actualDbId) {
+ if (!this.props.offline && actualDbId) {
this.setState({ schemaLoading: true });
const endpoint = `/superset/schemas/${actualDbId}/${forceRefresh}/`;
@@ -286,7 +289,13 @@ class SqlEditorLeftBar extends React.PureComponent {
}
}
+function mapStateToProps({ sqlLab }) {
+ return {
+ offline: sqlLab.offline,
+ };
+}
+
SqlEditorLeftBar.propTypes = propTypes;
SqlEditorLeftBar.defaultProps = defaultProps;
-export default SqlEditorLeftBar;
+export default connect(mapStateToProps)(SqlEditorLeftBar);
diff --git a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
index cf94b5f..e693295 100644
--- a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
+++ b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
@@ -21,9 +21,11 @@ const propTypes = {
tabHistory: PropTypes.array.isRequired,
tables: PropTypes.array.isRequired,
getHeight: PropTypes.func.isRequired,
+ offline: PropTypes.bool,
};
const defaultProps = {
queryEditors: [],
+ offline: false,
};
let queryCount = 1;
@@ -234,6 +236,7 @@ class TabbedSqlEditors extends React.PureComponent {
</div>
}
eventKey="add_tab"
+ disabled={this.props.offline}
/>
</Tabs>
);
@@ -250,6 +253,7 @@ function mapStateToProps({ sqlLab }) {
tabHistory: sqlLab.tabHistory,
tables: sqlLab.tables,
defaultDbId: sqlLab.defaultDbId,
+ offline: sqlLab.offline,
};
}
function mapDispatchToProps(dispatch) {
diff --git a/superset/assets/src/SqlLab/constants.js
b/superset/assets/src/SqlLab/constants.js
index 2a34112..1e98aa0 100644
--- a/superset/assets/src/SqlLab/constants.js
+++ b/superset/assets/src/SqlLab/constants.js
@@ -1,4 +1,5 @@
export const STATE_BSSTYLE_MAP = {
+ offline: 'danger',
failed: 'danger',
pending: 'info',
fetching: 'info',
@@ -8,12 +9,13 @@ export const STATE_BSSTYLE_MAP = {
success: 'success',
};
-export const STATUS_OPTIONS = [
- 'success',
- 'failed',
- 'running',
- 'pending',
-];
+export const STATUS_OPTIONS = {
+ success: 'success',
+ failed: 'failed',
+ running: 'running',
+ offline: 'offline',
+ pending: 'pending',
+};
export const TIME_OPTIONS = [
'now',
diff --git a/superset/assets/src/SqlLab/getInitialState.js
b/superset/assets/src/SqlLab/getInitialState.js
index 9c9210f..3848c99 100644
--- a/superset/assets/src/SqlLab/getInitialState.js
+++ b/superset/assets/src/SqlLab/getInitialState.js
@@ -16,14 +16,15 @@ export default function getInitialState({ defaultDbId,
...restBootstrapData }) {
return {
featureFlags: restBootstrapData.common.feature_flags,
sqlLab: {
+ activeSouthPaneTab: 'Results',
alerts: [],
- queries: {},
databases: {},
+ offline: false,
+ queries: {},
queryEditors: [defaultQueryEditor],
tabHistory: [defaultQueryEditor.id],
tables: [],
queriesLastUpdate: Date.now(),
- activeSouthPaneTab: 'Results',
...restBootstrapData,
},
messageToasts: getToastsFromPyFlashMessages(
diff --git a/superset/assets/src/SqlLab/reducers.js
b/superset/assets/src/SqlLab/reducers.js
index e357111..ed8ccff 100644
--- a/superset/assets/src/SqlLab/reducers.js
+++ b/superset/assets/src/SqlLab/reducers.js
@@ -250,6 +250,9 @@ export const sqlLabReducer = function (state = {}, action) {
}
return Object.assign({}, state, { queries: newQueries, queriesLastUpdate
});
},
+ [actions.SET_USER_OFFLINE]() {
+ return Object.assign({}, state, { offline: action.offline });
+ },
[actions.CREATE_DATASOURCE_STARTED]() {
return Object.assign({}, state, {
isDatasourceLoading: true,