diff --git a/superset/assets/javascripts/explorev2/actions/exploreActions.js b/superset/assets/javascripts/explorev2/actions/exploreActions.js index 08849e9b2fd20..a026d9094bd4b 100644 --- a/superset/assets/javascripts/explorev2/actions/exploreActions.js +++ b/superset/assets/javascripts/explorev2/actions/exploreActions.js @@ -98,41 +98,24 @@ export function setFieldValue(datasource_type, key, value, label) { return { type: SET_FIELD_VALUE, datasource_type, key, value, label }; } -export const UPDATE_CHART = 'UPDATE_CHART'; -export function updateChart(viz) { - return { type: UPDATE_CHART, viz }; -} - export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED'; export function chartUpdateStarted() { return { type: CHART_UPDATE_STARTED }; } -export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED '; +export const CHART_UPDATE_SUCCEEDED = 'CHART_UPDATE_SUCCEEDED'; +export function chartUpdateSucceeded(query) { + return { type: CHART_UPDATE_SUCCEEDED, query }; +} + +export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED'; export function chartUpdateFailed(error) { return { type: CHART_UPDATE_FAILED, error }; } -export function updateExplore(datasource_type, datasource_id, form_data) { - return function (dispatch) { - dispatch(chartUpdateStarted()); - const updateUrl = - `/superset/update_explore/${datasource_type}/${datasource_id}/`; - - $.ajax({ - type: 'POST', - url: updateUrl, - data: { - data: JSON.stringify(form_data), - }, - success: (data) => { - dispatch(updateChart(JSON.parse(data))); - }, - error(error) { - dispatch(chartUpdateFailed(error.responseJSON.error)); - }, - }); - }; +export const UPDATE_EXPLORE_ENDPOINTS = 'UPDATE_EXPLORE_ENDPOINTS'; +export function updateExploreEndpoints(jsonUrl, csvUrl, standaloneUrl) { + return { type: UPDATE_EXPLORE_ENDPOINTS, jsonUrl, csvUrl, standaloneUrl }; } export const REMOVE_CONTROL_PANEL_ALERT = 'REMOVE_CONTROL_PANEL_ALERT'; diff --git a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx index 7cb2711482dd2..ff59c35b9c01a 100644 --- a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx +++ b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx @@ -26,13 +26,13 @@ const propTypes = { json_endpoint: PropTypes.string.isRequired, csv_endpoint: PropTypes.string.isRequired, standalone_endpoint: PropTypes.string.isRequired, - query: PropTypes.string.isRequired, + query: PropTypes.string, column_formats: PropTypes.object, data: PropTypes.any, - chartStatus: PropTypes.bool, + chartStatus: PropTypes.string, isStarred: PropTypes.bool.isRequired, - chartUpdateStartTime: PropTypes.string.isRequired, - chartUpdateEndTime: PropTypes.string.isRequired, + chartUpdateStartTime: PropTypes.number.isRequired, + chartUpdateEndTime: PropTypes.number, alert: PropTypes.string, table_name: PropTypes.string, }; @@ -55,7 +55,15 @@ class ChartContainer extends React.Component { } componentWillReceiveProps(nextProps) { - this.setState({ mockSlice: this.getMockedSliceObject(nextProps) }); + if (nextProps.chartStatus !== this.props.chartStatus + || nextProps.height !== this.props.height) { + this.setState({ mockSlice: this.getMockedSliceObject(nextProps) }); + } + } + + shouldComponentUpdate(nextProps) { + return (nextProps.chartStatus !== this.props.chartStatus + || nextProps.height !== this.props.height); } componentDidUpdate() { @@ -114,8 +122,10 @@ class ChartContainer extends React.Component { {} ), - done: () => { + done: (payload) => { // finished rendering callback + // Todo: end timer and chartLoading set to success + props.actions.chartUpdateSucceeded(payload.query); }, clearError: () => { @@ -166,16 +176,19 @@ class ChartContainer extends React.Component { ); } - if (this.props.chartStatus === 'loading') { - return (loading); - } + const loading = this.props.chartStatus === 'loading'; return ( -
{ this.chartContainerRef = ref; }} - className={this.props.viz_type} - style={{ overflowX: 'scroll' }} - /> +
+ {loading && + loading + } +
{ this.chartContainerRef = ref; }} + className={this.props.viz_type} + style={{ overflowX: 'auto' }} + /> +
); } @@ -219,7 +232,7 @@ class ChartContainer extends React.Component { endTime={this.props.chartUpdateEndTime} isRunning={this.props.chartStatus === 'loading'} state={CHART_STATUS_MAP[this.props.chartStatus]} - style={{ 'font-size': '10px', 'margin-right': '5px' }} + style={{ fontSize: '10px', marginRight: '5px' }} />
diff --git a/superset/assets/javascripts/explorev2/exploreUtils.js b/superset/assets/javascripts/explorev2/exploreUtils.js index 52a9ed70444b6..15ac8e3517c37 100644 --- a/superset/assets/javascripts/explorev2/exploreUtils.js +++ b/superset/assets/javascripts/explorev2/exploreUtils.js @@ -1,4 +1,5 @@ /* eslint camelcase: 0 */ +const $ = require('jquery'); function formatFilters(filters) { // outputs an object of url params of filters // prefix can be 'flt' or 'having' @@ -22,7 +23,7 @@ export function getParamObject(form_data, datasource_type, saveNewSlice) { }; Object.keys(form_data).forEach((field) => { // filter out null fields - if (form_data[field] !== null && field !== 'datasource' + if (form_data[field] !== null && field !== 'datasource' && field !== 'filters' && !(saveNewSlice && field === 'slice_name')) { data[field] = form_data[field]; } @@ -31,3 +32,21 @@ export function getParamObject(form_data, datasource_type, saveNewSlice) { Object.assign(data, filterParams); return data; } + +export function getExploreUrl(form_data, datasource_type, endpoint = 'base') { + const data = getParamObject(form_data, datasource_type); + const params = `${datasource_type}/` + + `${form_data.datasource}/?${$.param(data, true)}`; + switch (endpoint) { + case 'base': + return `/superset/explore/${params}`; + case 'json': + return `/superset/explore_json/${params}`; + case 'csv': + return `/superset/explore/${params}&csv=true`; + case 'standalone': + return `/superset/explore/${params}&standalone=true`; + default: + return `/superset/explore/${params}`; + } +} diff --git a/superset/assets/javascripts/explorev2/reducers/exploreReducer.js b/superset/assets/javascripts/explorev2/reducers/exploreReducer.js index 4d8dc9d3637f7..176c0ecbca761 100644 --- a/superset/assets/javascripts/explorev2/reducers/exploreReducer.js +++ b/superset/assets/javascripts/explorev2/reducers/exploreReducer.js @@ -1,7 +1,9 @@ +/* eslint camelcase: 0 */ import { defaultFormData } from '../stores/store'; import * as actions from '../actions/exploreActions'; import { addToArr, removeFromArr, alterInArr } from '../../../utils/reducerUtils'; import { now } from '../../modules/dates'; +import { getExploreUrl } from '../exploreUtils'; export const exploreReducer = function (state, action) { const actionHandlers = { @@ -105,29 +107,35 @@ export const exploreReducer = function (state, action) { { viz: Object.assign({}, state.viz, { form_data: newFormData }) } ); }, - [actions.UPDATE_CHART]() { + [actions.CHART_UPDATE_SUCCEEDED]() { const vizUpdates = { - column_formats: action.viz.column_formats, - json_endpoint: action.viz.json_endpoint, - csv_endpoint: action.viz.csv_endpoint, - standalone_endpoint: action.viz.standalone_endpoint, - query: action.viz.query, - data: action.viz.data, + query: action.query, }; - const chartUpdateEndTime = now(); return Object.assign( {}, state, { - viz: Object.assign({}, state.viz, vizUpdates), chartStatus: 'success', - chartUpdateEndTime, + viz: Object.assign({}, state.viz, vizUpdates), }); }, [actions.CHART_UPDATE_STARTED]() { const chartUpdateStartTime = now(); + const form_data = Object.assign({}, state.viz.form_data); + const datasource_type = state.datasource_type; + const vizUpdates = { + json_endpoint: getExploreUrl(form_data, datasource_type, 'json'), + csv_endpoint: getExploreUrl(form_data, datasource_type, 'csv'), + standalone_endpoint: + getExploreUrl(form_data, datasource_type, 'standalone'), + }; return Object.assign({}, state, - { chartStatus: 'loading', chartUpdateEndTime: null, chartUpdateStartTime }); + { + chartStatus: 'loading', + chartUpdateEndTime: null, + chartUpdateStartTime, + viz: Object.assign({}, state.viz, vizUpdates), + }); }, [actions.CHART_UPDATE_FAILED]() { const chartUpdateEndTime = now(); diff --git a/superset/views.py b/superset/views.py index 55b570dfec258..92abaec4aa4db 100755 --- a/superset/views.py +++ b/superset/views.py @@ -1364,28 +1364,6 @@ def slice(self, slice_id): viz_obj = self.get_viz(slice_id) return redirect(viz_obj.get_url(**request.args)) - @log_this - @has_access_api - @expose( - "/update_explore///", methods=['POST']) - def update_explore(self, datasource_type, datasource_id): - """Send back new viz on POST request for updating update explore view""" - form_data = json.loads(request.form.get('data')) - try: - viz_obj = self.get_viz( - datasource_type=datasource_type, - datasource_id=datasource_id, - args=form_data) - except Exception as e: - logging.exception(e) - return json_error_response('{}'.format(e)) - try: - viz_json = viz_obj.get_json() - except Exception as e: - logging.exception(e) - return json_error_response(utils.error_msg_from_exception(e)) - return viz_json - @has_access_api @expose("/explore_json///") def explore_json(self, datasource_type, datasource_id): diff --git a/tests/base_tests.py b/tests/base_tests.py index 2a2e627b45b2e..3ff744fc9bd86 100644 --- a/tests/base_tests.py +++ b/tests/base_tests.py @@ -265,7 +265,6 @@ def assert_can_all(view_menu): self.assertIn(('can_fave_slices', 'Superset'), gamma_perm_set) self.assertIn(('can_save_dash', 'Superset'), gamma_perm_set) self.assertIn(('can_slice', 'Superset'), gamma_perm_set) - self.assertIn(('can_update_explore', 'Superset'), gamma_perm_set) diff --git a/tests/core_tests.py b/tests/core_tests.py index 3f73a975dfe60..c7f05f4e7907e 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -95,25 +95,6 @@ def assert_admin_view_menus_in(role_name, assert_func): assert_admin_view_menus_in('Alpha', self.assertNotIn) assert_admin_view_menus_in('Gamma', self.assertNotIn) - def test_update_explore(self): - self.login(username='admin') - tbl_id = self.table_ids.get('energy_usage') - data = json.dumps({ - 'viz_type': 'sankey', - 'groupby': ['source', 'target'], - 'metrics': ['sum__value'], - 'row_limit': 5000, - 'flt_col_0': 'source', - 'datasource_name': 'energy_usage', - 'datasource_id': tbl_id, - 'datasource_type': 'table', - 'previous_viz_type': 'sankey' - }) - response = self.client.post('/superset/update_explore/table/{}/'.format(tbl_id), - data=dict(data=data)) - assert response.status_code == 200 - self.logout() - def test_save_slice(self): self.login(username='admin') slice_name = "Energy Sankey"