| /** |
| * 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; |