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

[Sqllab] Add offline state to sqllab #6013

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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);
timifasubaa marked this conversation as resolved.
Show resolved Hide resolved

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 });

Choose a reason for hiding this comment

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

before .setProps, can you add an expect that disabled props toBe(false)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

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