blob: 74aa900b7110bbcf78667f24e7cc0d547c9c544b [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import Loader from './LoaderComponent';
import AdhocQueryStore from '../stores/AdhocQueryStore';
import AdhocQueryActions from '../actions/AdhocQueryActions';
import UserStore from '../stores/UserStore';
import QueryPreview from './QueryPreviewComponent';
let interval = null;
function isResultAvailableOnServer (handle) {
// always check before polling
let query = AdhocQueryStore.getQueries()[handle];
if (query && query.status && query.status.status === 'SUCCESSFUL') {
return true;
}
return false;
}
function fetchResult (secretToken, handle) {
if (isResultAvailableOnServer(handle)) {
let query = AdhocQueryStore.getQueries()[handle];
let mode = query.isPersistent ? 'PERSISTENT' : 'INMEMORY';
AdhocQueryActions.getQueryResult(secretToken, handle, mode);
} else {
AdhocQueryActions.getQuery(secretToken, handle);
}
}
function constructTable (tableData) {
if (!tableData.columns && !tableData.results) return;
let header = tableData.columns.map(column => {
return <th>{ column.name }</th>;
});
let rows = tableData.results
.map(row => {
return (<tr>{row.values.map(cell => {
return <td>{(cell && cell.value) || <span style={{color: 'red'}}>NULL</span>}</td>;
})}</tr>);
});
// in case the results are empty, happens when LENS server has restarted
// all in-memory results are wiped clean
if (!rows.length) {
let colWidth = tableData.columns.length;
rows = <tr>
<td colSpan={colWidth} style={{color: 'red', textAlign: 'center'}}>
Result set no longer available with server.</td>
</tr>;
}
return (
<div className='table-responsive'>
<table className='table table-striped table-condensed'>
<thead>
<tr>{header}</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
);
}
class QueryDetailResult extends React.Component {
constructor (props) {
super(props);
this.state = { loading: true, queryResult: {}, query: null };
this._onChange = this._onChange.bind(this);
this.pollForResult = this.pollForResult.bind(this);
}
componentDidMount () {
let secretToken = UserStore.getUserDetails().secretToken;
this.pollForResult(secretToken, this.props.params.handle);
AdhocQueryStore.addChangeListener(this._onChange);
}
componentWillUnmount () {
clearInterval(interval);
AdhocQueryStore.removeChangeListener(this._onChange);
}
componentWillReceiveProps (props) {
this.state = { loading: true, queryResult: {}, query: null };
let secretToken = UserStore.getUserDetails().secretToken;
clearInterval(interval);
this.pollForResult(secretToken, props.params.handle);
}
render () {
let query = this.state.query;
let queryResult = this.state.queryResult;
let result = '';
// check if the query was persistent or in-memory
if (query && query.isPersistent && query.status.status === 'SUCCESSFUL') {
result = (<div className='text-center'>
<a href={queryResult.downloadURL} download>
<span className='glyphicon glyphicon-download-alt '></span> Click
here to download the results as a CSV file
</a>
</div>);
} else {
result = constructTable(this.state.queryResult);
}
if (this.state.loading) result = <Loader size='8px' margin='2px' />;
return (
<div className='panel panel-default'>
<div className='panel-heading'>
<h3 className='panel-title'>Query Result</h3>
</div>
<div className='panel-body no-padding'>
<div>
<QueryPreview key={query && query.queryHandle.handleId}
{...query} />
</div>
{result}
</div>
</div>
);
}
pollForResult (secretToken, handle) {
// fetch results immediately if present, don't wait for 5 seconds
// in setInterval below.
// FIXME if I put a return in if construct, setInterval won't execute which
// shouldn't but the backend API isn't stable enough, and if this call fails
// we'll not be able to show the results and it'll show a loader, thoughts?
fetchResult(secretToken, handle);
interval = setInterval(function () {
fetchResult(secretToken, handle);
}, 5000);
}
_onChange () {
let handle = this.props.params.handle;
let query = AdhocQueryStore.getQueries()[handle];
let result = AdhocQueryStore.getQueryResult(handle);
let loading = true;
let failed = query && query.status && query.status.status === 'FAILED';
let success = query && query.status && query.status.status === 'SUCCESSFUL';
if (failed || success && result) {
clearInterval(interval);
loading = false;
}
// check first if the query failed, clear the interval, and show it
// setState when query is successful AND we've the results OR it failed
let state = {
loading: loading,
queryResult: result || {}, // result can be undefined so guarding it
query: query
};
this.setState(state);
}
}
QueryDetailResult.propTypes = {
query: React.PropTypes.object,
params: React.PropTypes.object
};
export default QueryDetailResult;