2019-01-17 10:59:47 -06:00
|
|
|
// Libraries
|
2019-01-13 16:10:23 -06:00
|
|
|
import React, { PureComponent } from 'react';
|
2019-01-17 10:59:47 -06:00
|
|
|
import _ from 'lodash';
|
2019-01-13 16:10:23 -06:00
|
|
|
import { hot } from 'react-hot-loader';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
|
2019-01-17 10:59:47 -06:00
|
|
|
// Components
|
2019-01-13 16:10:23 -06:00
|
|
|
import QueryEditor from './QueryEditor';
|
|
|
|
import QueryTransactionStatus from './QueryTransactionStatus';
|
2019-01-17 10:59:47 -06:00
|
|
|
|
|
|
|
// Actions
|
2019-02-04 00:47:10 -06:00
|
|
|
import { changeQuery, modifyQueries, runQueries, addQueryRow } from './state/actions';
|
2019-01-13 16:10:23 -06:00
|
|
|
|
2019-01-17 10:59:47 -06:00
|
|
|
// Types
|
|
|
|
import { StoreState } from 'app/types';
|
2019-02-01 05:54:16 -06:00
|
|
|
import { RawTimeRange, DataQuery, ExploreDataSourceApi, QueryHint, QueryFixAction } from '@grafana/ui';
|
2019-01-17 12:17:29 -06:00
|
|
|
import { QueryTransaction, HistoryItem, ExploreItemState, ExploreId } from 'app/types/explore';
|
2019-01-17 10:59:47 -06:00
|
|
|
import { Emitter } from 'app/core/utils/emitter';
|
2019-02-04 00:47:10 -06:00
|
|
|
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
|
2019-01-17 10:59:47 -06:00
|
|
|
|
2019-01-13 16:10:23 -06:00
|
|
|
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
|
|
|
|
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
|
|
|
|
if (transaction) {
|
|
|
|
return transaction.hints[0];
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface QueryRowProps {
|
|
|
|
addQueryRow: typeof addQueryRow;
|
|
|
|
changeQuery: typeof changeQuery;
|
|
|
|
className?: string;
|
|
|
|
exploreId: ExploreId;
|
2019-02-01 04:55:01 -06:00
|
|
|
datasourceInstance: ExploreDataSourceApi;
|
2019-02-04 00:47:10 -06:00
|
|
|
highlightLogsExpressionAction: typeof highlightLogsExpressionAction;
|
2019-01-13 16:10:23 -06:00
|
|
|
history: HistoryItem[];
|
|
|
|
index: number;
|
2019-02-04 23:19:40 -06:00
|
|
|
query: DataQuery;
|
2019-01-13 16:10:23 -06:00
|
|
|
modifyQueries: typeof modifyQueries;
|
|
|
|
queryTransactions: QueryTransaction[];
|
|
|
|
exploreEvents: Emitter;
|
|
|
|
range: RawTimeRange;
|
2019-02-04 00:47:10 -06:00
|
|
|
removeQueryRowAction: typeof removeQueryRowAction;
|
2019-01-13 16:10:23 -06:00
|
|
|
runQueries: typeof runQueries;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class QueryRow extends PureComponent<QueryRowProps> {
|
|
|
|
onExecuteQuery = () => {
|
|
|
|
const { exploreId } = this.props;
|
|
|
|
this.props.runQueries(exploreId);
|
|
|
|
};
|
|
|
|
|
|
|
|
onChangeQuery = (query: DataQuery, override?: boolean) => {
|
|
|
|
const { datasourceInstance, exploreId, index } = this.props;
|
|
|
|
this.props.changeQuery(exploreId, query, index, override);
|
|
|
|
if (query && !override && datasourceInstance.getHighlighterExpression && index === 0) {
|
|
|
|
// Live preview of log search matches. Only use on first row for now
|
|
|
|
this.updateLogsHighlights(query);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-23 10:44:22 -06:00
|
|
|
componentWillUnmount() {
|
|
|
|
console.log('QueryRow will unmount');
|
|
|
|
}
|
|
|
|
|
2019-01-13 16:10:23 -06:00
|
|
|
onClickAddButton = () => {
|
|
|
|
const { exploreId, index } = this.props;
|
|
|
|
this.props.addQueryRow(exploreId, index);
|
|
|
|
};
|
|
|
|
|
|
|
|
onClickClearButton = () => {
|
|
|
|
this.onChangeQuery(null, true);
|
|
|
|
};
|
|
|
|
|
2019-02-01 05:54:16 -06:00
|
|
|
onClickHintFix = (action: QueryFixAction) => {
|
2019-01-13 16:10:23 -06:00
|
|
|
const { datasourceInstance, exploreId, index } = this.props;
|
|
|
|
if (datasourceInstance && datasourceInstance.modifyQuery) {
|
2019-02-01 05:54:16 -06:00
|
|
|
const modifier = (queries: DataQuery, action: QueryFixAction) => datasourceInstance.modifyQuery(queries, action);
|
2019-01-13 16:10:23 -06:00
|
|
|
this.props.modifyQueries(exploreId, action, index, modifier);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
onClickRemoveButton = () => {
|
|
|
|
const { exploreId, index } = this.props;
|
2019-02-04 00:47:10 -06:00
|
|
|
this.props.removeQueryRowAction({ exploreId, index });
|
2019-01-13 16:10:23 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
updateLogsHighlights = _.debounce((value: DataQuery) => {
|
|
|
|
const { datasourceInstance } = this.props;
|
|
|
|
if (datasourceInstance.getHighlighterExpression) {
|
2019-02-04 00:47:10 -06:00
|
|
|
const { exploreId } = this.props;
|
2019-01-13 16:10:23 -06:00
|
|
|
const expressions = [datasourceInstance.getHighlighterExpression(value)];
|
2019-02-04 00:47:10 -06:00
|
|
|
this.props.highlightLogsExpressionAction({ exploreId, expressions });
|
2019-01-13 16:10:23 -06:00
|
|
|
}
|
|
|
|
}, 500);
|
|
|
|
|
|
|
|
render() {
|
2019-02-04 23:19:40 -06:00
|
|
|
const { datasourceInstance, history, index, query, queryTransactions, exploreEvents, range } = this.props;
|
2019-01-13 16:10:23 -06:00
|
|
|
const transactions = queryTransactions.filter(t => t.rowIndex === index);
|
|
|
|
const transactionWithError = transactions.find(t => t.error !== undefined);
|
|
|
|
const hint = getFirstHintFromTransactions(transactions);
|
|
|
|
const queryError = transactionWithError ? transactionWithError.error : null;
|
|
|
|
const QueryField = datasourceInstance.pluginExports.ExploreQueryField;
|
|
|
|
return (
|
|
|
|
<div className="query-row">
|
|
|
|
<div className="query-row-status">
|
|
|
|
<QueryTransactionStatus transactions={transactions} />
|
|
|
|
</div>
|
2019-02-04 10:26:20 -06:00
|
|
|
<div className="query-row-field flex-shrink-1">
|
2019-01-13 16:10:23 -06:00
|
|
|
{QueryField ? (
|
|
|
|
<QueryField
|
|
|
|
datasource={datasourceInstance}
|
2019-02-04 23:19:40 -06:00
|
|
|
query={query}
|
2019-01-13 16:10:23 -06:00
|
|
|
error={queryError}
|
|
|
|
hint={hint}
|
|
|
|
history={history}
|
2019-02-01 05:54:16 -06:00
|
|
|
onExecuteQuery={this.onExecuteQuery}
|
|
|
|
onExecuteHint={this.onClickHintFix}
|
2019-01-13 16:10:23 -06:00
|
|
|
onQueryChange={this.onChangeQuery}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<QueryEditor
|
|
|
|
datasource={datasourceInstance}
|
|
|
|
error={queryError}
|
|
|
|
onQueryChange={this.onChangeQuery}
|
|
|
|
onExecuteQuery={this.onExecuteQuery}
|
2019-02-04 23:19:40 -06:00
|
|
|
initialQuery={query}
|
2019-01-13 16:10:23 -06:00
|
|
|
exploreEvents={exploreEvents}
|
|
|
|
range={range}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
2019-02-04 10:26:20 -06:00
|
|
|
<div className="gf-form-inline flex-shrink-0">
|
2019-01-29 01:14:07 -06:00
|
|
|
<div className="gf-form">
|
|
|
|
<button className="gf-form-label gf-form-label--btn" onClick={this.onClickClearButton}>
|
|
|
|
<i className="fa fa-times" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div className="gf-form">
|
|
|
|
<button className="gf-form-label gf-form-label--btn" onClick={this.onClickAddButton}>
|
|
|
|
<i className="fa fa-plus" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div className="gf-form">
|
|
|
|
<button className="gf-form-label gf-form-label--btn" onClick={this.onClickRemoveButton}>
|
|
|
|
<i className="fa fa-minus" />
|
|
|
|
</button>
|
|
|
|
</div>
|
2019-01-13 16:10:23 -06:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function mapStateToProps(state: StoreState, { exploreId, index }) {
|
|
|
|
const explore = state.explore;
|
|
|
|
const item: ExploreItemState = explore[exploreId];
|
2019-02-04 23:19:40 -06:00
|
|
|
const { datasourceInstance, history, queries, queryTransactions, range } = item;
|
|
|
|
const query = queries[index];
|
|
|
|
return { datasourceInstance, history, query, queryTransactions, range };
|
2019-01-13 16:10:23 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
const mapDispatchToProps = {
|
|
|
|
addQueryRow,
|
|
|
|
changeQuery,
|
2019-02-04 00:47:10 -06:00
|
|
|
highlightLogsExpressionAction,
|
2019-01-13 16:10:23 -06:00
|
|
|
modifyQueries,
|
2019-02-04 00:47:10 -06:00
|
|
|
removeQueryRowAction,
|
2019-01-13 16:10:23 -06:00
|
|
|
runQueries,
|
|
|
|
};
|
|
|
|
|
|
|
|
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(QueryRow));
|