Skip to content

Commit

Permalink
[Sqllab] Add offline state to sqllab (apache#6013)
Browse files Browse the repository at this point in the history
* add timeout and refresh for failed backend

* show offline state instead of refreshing

* add southpane tests
  • Loading branch information
timifasubaa authored Oct 23, 2018
1 parent b9257b2 commit fc3b68e
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 17 deletions.
37 changes: 37 additions & 0 deletions superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
Original file line number Diff line number Diff line change
@@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
5 changes: 5 additions & 0 deletions superset/assets/src/SqlLab/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 };
}
Expand Down
7 changes: 6 additions & 1 deletion superset/assets/src/SqlLab/components/QueryAutoRefresh.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion superset/assets/src/SqlLab/components/QuerySearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
12 changes: 11 additions & 1 deletion superset/assets/src/SqlLab/components/SouthPane.jsx
Original file line number Diff line number Diff line change
@@ -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';

/*
Expand All @@ -21,17 +22,25 @@ 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 {
switchTab(id) {
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;
Expand Down Expand Up @@ -103,6 +112,7 @@ function mapStateToProps({ sqlLab }) {
return {
activeSouthPaneTab: sqlLab.activeSouthPaneTab,
databases: sqlLab.databases,
offline: sqlLab.offline,
};
}

Expand Down
17 changes: 13 additions & 4 deletions superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 {
Expand Down Expand Up @@ -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: [] });
}

Expand All @@ -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}/`;

Expand Down Expand Up @@ -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}/`;

Expand Down Expand Up @@ -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);
4 changes: 4 additions & 0 deletions superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -234,6 +236,7 @@ class TabbedSqlEditors extends React.PureComponent {
</div>
}
eventKey="add_tab"
disabled={this.props.offline}
/>
</Tabs>
);
Expand All @@ -250,6 +253,7 @@ function mapStateToProps({ sqlLab }) {
tabHistory: sqlLab.tabHistory,
tables: sqlLab.tables,
defaultDbId: sqlLab.defaultDbId,
offline: sqlLab.offline,
};
}
function mapDispatchToProps(dispatch) {
Expand Down
14 changes: 8 additions & 6 deletions superset/assets/src/SqlLab/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const STATE_BSSTYLE_MAP = {
offline: 'danger',
failed: 'danger',
pending: 'info',
fetching: 'info',
Expand All @@ -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',
Expand Down
5 changes: 3 additions & 2 deletions superset/assets/src/SqlLab/getInitialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions superset/assets/src/SqlLab/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit fc3b68e

Please sign in to comment.