diff --git a/ui/src/admin/actions/index.js b/ui/src/admin/actions/index.js
index 122b2b07d..5b0db3331 100644
--- a/ui/src/admin/actions/index.js
+++ b/ui/src/admin/actions/index.js
@@ -1,4 +1,5 @@
import {getUsers, getRoles} from 'src/admin/apis'
+import {killQuery as killQueryProxy} from 'shared/apis/metaQuery'
export const loadUsers = ({users}) => ({
type: 'LOAD_USERS',
@@ -7,6 +8,28 @@ export const loadUsers = ({users}) => ({
},
})
+export const killQuery = (queryID) => ({
+ type: 'KILL_QUERY',
+ payload: {
+ queryID,
+ },
+})
+
+export const setQueryToKill = (queryIDToKill) => ({
+ type: 'SET_QUERY_TO_KILL',
+ payload: {
+ queryIDToKill,
+ },
+})
+
+export const loadQueries = (queries) => ({
+ type: 'LOAD_QUERIES',
+ payload: {
+ queries,
+ },
+})
+
+// async actions
export const loadUsersAsync = (url) => async (dispatch) => {
const {data} = await getUsers(url)
dispatch(loadUsers(data))
@@ -22,3 +45,12 @@ export const loadRolesAsync = (url) => async (dispatch) => {
const {data} = await getRoles(url)
dispatch(loadRoles(data))
}
+
+export const killQueryAsync = (source, queryID) => (dispatch) => {
+ // optimistic update
+ dispatch(killQuery(queryID))
+ dispatch(setQueryToKill(null))
+
+ // kill query on server
+ killQueryProxy(source, queryID)
+}
diff --git a/ui/src/admin/containers/QueriesPage.js b/ui/src/admin/containers/QueriesPage.js
index c3b0a5e6b..348e9125d 100644
--- a/ui/src/admin/containers/QueriesPage.js
+++ b/ui/src/admin/containers/QueriesPage.js
@@ -1,51 +1,64 @@
import React, {PropTypes, Component} from 'react'
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+
import flatten from 'lodash/flatten'
-import reject from 'lodash/reject'
import uniqBy from 'lodash/uniqBy'
+
import {
showDatabases,
showQueries,
- killQuery,
} from 'shared/apis/metaQuery'
import QueriesTable from 'src/admin/components/QueriesTable'
import showDatabasesParser from 'shared/parsing/showDatabases'
import showQueriesParser from 'shared/parsing/showQueries'
import {TIMES} from 'src/admin/constants'
+import {
+ loadQueries as loadQueriesAction,
+ setQueryToKill as setQueryToKillAction,
+ killQueryAsync,
+} from 'src/admin/actions'
-export default class QueriesPage extends Component {
+class QueriesPage extends Component {
constructor(props) {
super(props)
- this.state = {
- queries: [],
- queryIDToKill: null,
- }
-
- this.updateQueries = this.updateQueries.bind(this);
- this.handleConfirmKillQuery = this.handleConfirmKillQuery.bind(this);
- this.handleKillQuery = this.handleKillQuery.bind(this);
+ this.updateQueries = this.updateQueries.bind(this)
+ this.handleConfirmKillQuery = this.handleConfirmKillQuery.bind(this)
+ this.handleKillQuery = this.handleKillQuery.bind(this)
}
componentDidMount() {
- this.updateQueries();
- const updateInterval = 5000;
- this.intervalID = setInterval(this.updateQueries, updateInterval);
+ this.updateQueries()
+ const updateInterval = 5000
+ this.intervalID = setInterval(this.updateQueries, updateInterval)
}
componentWillUnmount() {
- clearInterval(this.intervalID);
+ clearInterval(this.intervalID)
+ }
+
+ render() {
+ const {queries} = this.props;
+
+ return (
+
+
+
+
+ );
}
updateQueries() {
- const {source, addFlashMessage} = this.props;
+ const {source, addFlashMessage, loadQueries} = this.props
showDatabases(source.links.proxy).then((resp) => {
- const {databases, errors} = showDatabasesParser(resp.data);
+ const {databases, errors} = showDatabasesParser(resp.data)
if (errors.length) {
- errors.forEach((message) => addFlashMessage({type: 'error', text: message}));
+ errors.forEach((message) => addFlashMessage({type: 'error', text: message}))
return;
}
- const fetches = databases.map((db) => showQueries(source.links.proxy, db));
+ const fetches = databases.map((db) => showQueries(source.links.proxy, db))
Promise.all(fetches).then((queryResponses) => {
const allQueries = [];
@@ -66,50 +79,26 @@ export default class QueriesPage extends Component {
const bTime = TIMES.find((t) => b.duration.match(t.test));
return +aTime.magnitude <= +bTime.magnitude;
});
- this.setState({
- queries: sortedQueries,
- });
+
+ loadQueries(sortedQueries)
});
});
}
- render() {
- const {queries} = this.state;
- return (
-
-
-
-
- );
- }
-
handleKillQuery(e) {
e.stopPropagation();
const id = e.target.dataset.queryId;
- this.setState({
- queryIDToKill: id,
- });
+
+ this.props.setQueryToKill(id)
}
handleConfirmKillQuery() {
- const {queryIDToKill} = this.state;
+ const {queryIDToKill, source, killQuery} = this.props;
if (queryIDToKill === null) {
return;
}
- // optimitstic update
- const {queries} = this.state;
- this.setState({
- queries: reject(queries, (q) => +q.id === +queryIDToKill),
- });
-
- // kill the query over http
- const {source} = this.props;
- killQuery(source.links.proxy, queryIDToKill).then(() => {
- this.setState({
- queryIDToKill: null,
- });
- });
+ killQuery(source.links.proxy, queryIDToKill)
}
}
@@ -126,6 +115,7 @@ const QueriesHeader = () => (
)
const {
+ arrayOf,
func,
string,
shape,
@@ -137,5 +127,23 @@ QueriesPage.propTypes = {
proxy: string,
}),
}),
+ queries: arrayOf(shape()),
addFlashMessage: func,
+ loadQueries: func,
+ queryIDToKill: string,
+ setQueryToKill: func,
+ killQuery: func,
}
+
+const mapStateToProps = ({admin: {queries, queryIDToKill}}) => ({
+ queries,
+ queryIDToKill,
+})
+
+const mapDispatchToProps = (dispatch) => ({
+ loadQueries: bindActionCreators(loadQueriesAction, dispatch),
+ setQueryToKill: bindActionCreators(setQueryToKillAction, dispatch),
+ killQuery: bindActionCreators(killQueryAsync, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(QueriesPage)
diff --git a/ui/src/admin/reducers/admin.js b/ui/src/admin/reducers/admin.js
index 3e8c8d49e..c23ecb6c0 100644
--- a/ui/src/admin/reducers/admin.js
+++ b/ui/src/admin/reducers/admin.js
@@ -1,6 +1,10 @@
+import reject from 'lodash/reject'
+
const initialState = {
users: [],
roles: [],
+ queries: [],
+ queryIDToKill: null,
}
export default function admin(state = initialState, action) {
@@ -12,6 +16,23 @@ export default function admin(state = initialState, action) {
case 'LOAD_ROLES': {
return {...state, ...action.payload}
}
+
+ case 'LOAD_QUERIES': {
+ return {...state, ...action.payload}
+ }
+
+ case 'KILL_QUERY': {
+ const {queryID} = action.payload
+ const nextState = {
+ queries: reject(state.queries, (q) => +q.id === +queryID),
+ }
+
+ return {...state, ...nextState}
+ }
+
+ case 'SET_QUERY_TO_KILL': {
+ return {...state, ...action.payload}
+ }
}
return state
diff --git a/ui/src/utils/queryUrlGenerator.js b/ui/src/utils/queryUrlGenerator.js
index c6153ea87..eecf77e05 100644
--- a/ui/src/utils/queryUrlGenerator.js
+++ b/ui/src/utils/queryUrlGenerator.js
@@ -1,13 +1,17 @@
import AJAX from 'utils/ajax';
-export function proxy({source, query, db, rp}) {
- return AJAX({
- method: 'POST',
- url: source,
- data: {
- query,
- db,
- rp,
- },
- });
+export const proxy = async ({source, query, db, rp}) => {
+ try {
+ return await AJAX({
+ method: 'POST',
+ url: source,
+ data: {
+ query,
+ db,
+ rp,
+ },
+ })
+ } catch (error) {
+ console.error(error) // eslint-disable-line no-console
+ }
}