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 <30888507+timifasu...@users.noreply.github.com>
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,

Reply via email to