mirror of https://github.com/grafana/grafana
Alerting: adding query editor when creating threshold rule. (#33123)
* fix viz * add datasource picker on query rows in mixed mode * add timerange, handle add/remove queryrunners * multiqueryrunner test * trying things out. * adding another test to verify running a induvidual query runner will update multirunner. * cleaned up tests a bit. * draft version working ok. * fixing so we base the refId from request targets. * reenable adding expression * layout fixes for alerting page * some cleanup * cleaning up code that we won't use * changed so we don't display the time range if params not passed. * remove unused things in querygroup * changed button to type button. * removed timerange from dataQuery and removed multiquery runner. * minor refactoring. * renamed callback function to make it more clear what it does. * renamed droppable area. * changed so we only display the query editor when selecting threshold. * removed the refresh picker. * revert * wip * extending with data query. * timerange fixes * it is now possible to add grafana queries. * removed unused type. * removed expect import. * added docs. * moved range converting methods to rangeUtil. * clean up some typings, remove file * making sure we don't blow up on component being unmounted. Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>pull/33222/head
parent
a151dfaa04
commit
569fb3f112
@ -0,0 +1,143 @@ |
||||
import React, { PureComponent } from 'react'; |
||||
import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; |
||||
import { DataQuery, DataSourceApi, DataSourceInstanceSettings, rangeUtil, PanelData, TimeRange } from '@grafana/data'; |
||||
import { getDataSourceSrv } from '@grafana/runtime'; |
||||
import { QueryEditorRow } from 'app/features/query/components/QueryEditorRow'; |
||||
import { isExpressionQuery } from 'app/features/expressions/guards'; |
||||
import { GrafanaQuery } from 'app/types/unified-alerting-dto'; |
||||
|
||||
interface Props { |
||||
// The query configuration
|
||||
queries: GrafanaQuery[]; |
||||
|
||||
// Query editing
|
||||
onQueriesChange: (queries: GrafanaQuery[]) => void; |
||||
onDuplicateQuery: (query: GrafanaQuery) => void; |
||||
onRunQueries: () => void; |
||||
} |
||||
|
||||
interface State { |
||||
dataPerQuery: Record<string, PanelData>; |
||||
defaultDataSource: DataSourceApi; |
||||
} |
||||
|
||||
export class AlertingQueryRows extends PureComponent<Props, State> { |
||||
constructor(props: Props) { |
||||
super(props); |
||||
this.state = { dataPerQuery: {}, defaultDataSource: {} as DataSourceApi }; |
||||
} |
||||
|
||||
async componentDidMount() { |
||||
const defaultDataSource = await getDataSourceSrv().get(); |
||||
this.setState({ defaultDataSource }); |
||||
} |
||||
|
||||
onRemoveQuery = (query: DataQuery) => { |
||||
this.props.onQueriesChange(this.props.queries.filter((item) => item.model !== query)); |
||||
}; |
||||
|
||||
onChangeTimeRange(timeRange: TimeRange, index: number) { |
||||
const { queries, onQueriesChange } = this.props; |
||||
onQueriesChange( |
||||
queries.map((item, itemIndex) => { |
||||
if (itemIndex === index) { |
||||
return { ...item, relativeTimeRange: rangeUtil.timeRangeToRelative(timeRange) }; |
||||
} |
||||
return item; |
||||
}) |
||||
); |
||||
} |
||||
|
||||
onChangeQuery(query: DataQuery, index: number) { |
||||
const { queries, onQueriesChange } = this.props; |
||||
onQueriesChange( |
||||
queries.map((item, itemIndex) => { |
||||
if (itemIndex === index) { |
||||
return { ...item, model: { ...item.model, ...query, datasource: query.datasource! } }; |
||||
} |
||||
return item; |
||||
}) |
||||
); |
||||
} |
||||
|
||||
onDragEnd = (result: DropResult) => { |
||||
const { queries, onQueriesChange } = this.props; |
||||
|
||||
if (!result || !result.destination) { |
||||
return; |
||||
} |
||||
|
||||
const startIndex = result.source.index; |
||||
const endIndex = result.destination.index; |
||||
if (startIndex === endIndex) { |
||||
return; |
||||
} |
||||
|
||||
const update = Array.from(queries); |
||||
const [removed] = update.splice(startIndex, 1); |
||||
update.splice(endIndex, 0, removed); |
||||
onQueriesChange(update); |
||||
}; |
||||
|
||||
getDataSourceSettings = (query: DataQuery): DataSourceInstanceSettings | undefined => { |
||||
const { defaultDataSource } = this.state; |
||||
|
||||
if (isExpressionQuery(query)) { |
||||
return getDataSourceSrv().getInstanceSettings(defaultDataSource.name); |
||||
} |
||||
|
||||
return getDataSourceSrv().getInstanceSettings(query.datasource); |
||||
}; |
||||
|
||||
render() { |
||||
const { queries } = this.props; |
||||
|
||||
return ( |
||||
<DragDropContext onDragEnd={this.onDragEnd}> |
||||
<Droppable droppableId="alerting-queries" direction="vertical"> |
||||
{(provided) => { |
||||
return ( |
||||
<div ref={provided.innerRef} {...provided.droppableProps}> |
||||
{queries.map((query: GrafanaQuery, index) => { |
||||
const data = this.state.dataPerQuery[query.refId]; |
||||
const dsSettings = this.getDataSourceSettings(query); |
||||
|
||||
if (!dsSettings) { |
||||
return null; |
||||
} |
||||
|
||||
return ( |
||||
<QueryEditorRow |
||||
dsSettings={{ ...dsSettings, meta: { ...dsSettings.meta, mixed: true } }} |
||||
id={query.refId} |
||||
index={index} |
||||
key={query.refId} |
||||
data={data} |
||||
query={query.model} |
||||
onChange={(query) => this.onChangeQuery(query, index)} |
||||
timeRange={ |
||||
!isExpressionQuery(query.model) |
||||
? rangeUtil.relativeToTimeRange(query.relativeTimeRange) |
||||
: undefined |
||||
} |
||||
onChangeTimeRange={ |
||||
!isExpressionQuery(query.model) |
||||
? (timeRange) => this.onChangeTimeRange(timeRange, index) |
||||
: undefined |
||||
} |
||||
onRemoveQuery={this.onRemoveQuery} |
||||
onAddQuery={this.props.onDuplicateQuery} |
||||
onRunQuery={this.props.onRunQueries} |
||||
queries={queries} |
||||
/> |
||||
); |
||||
})} |
||||
{provided.placeholder} |
||||
</div> |
||||
); |
||||
}} |
||||
</Droppable> |
||||
</DragDropContext> |
||||
); |
||||
} |
||||
} |
||||
@ -1,27 +0,0 @@ |
||||
import { TextArea } from '@grafana/ui'; |
||||
import { GrafanaQuery } from 'app/types/unified-alerting-dto'; |
||||
import React, { FC, useState } from 'react'; |
||||
|
||||
interface Props { |
||||
value?: GrafanaQuery[]; |
||||
onChange: (value: GrafanaQuery[]) => void; |
||||
} |
||||
|
||||
// @TODO replace with actual query editor once it's done
|
||||
export const GrafanaQueryEditor: FC<Props> = ({ value, onChange }) => { |
||||
const [content, setContent] = useState(JSON.stringify(value || [], null, 2)); |
||||
const onChangeHandler = (e: React.FormEvent<HTMLTextAreaElement>) => { |
||||
const val = (e.target as HTMLTextAreaElement).value; |
||||
setContent(val); |
||||
try { |
||||
const parsed = JSON.parse(val); |
||||
if (parsed && Array.isArray(parsed)) { |
||||
console.log('queries changed'); |
||||
onChange(parsed); |
||||
} |
||||
} catch (e) { |
||||
console.log('invalid json'); |
||||
} |
||||
}; |
||||
return <TextArea rows={20} value={content} onChange={onChangeHandler} />; |
||||
}; |
||||
@ -0,0 +1,7 @@ |
||||
import { DataQuery } from '@grafana/data'; |
||||
import { ExpressionDatasourceID } from './ExpressionDatasource'; |
||||
import { ExpressionQuery } from './types'; |
||||
|
||||
export const isExpressionQuery = (dataQuery?: DataQuery): dataQuery is ExpressionQuery => { |
||||
return dataQuery?.datasource === ExpressionDatasourceID; |
||||
}; |
||||
Loading…
Reference in new issue