diff --git a/src/HaystackVariableSupport.ts b/src/HaystackVariableSupport.ts new file mode 100644 index 0000000..e88a19e --- /dev/null +++ b/src/HaystackVariableSupport.ts @@ -0,0 +1,91 @@ +import { + CustomVariableSupport, + DataQueryRequest, + DataQueryResponse, + DataFrame, + Field, + FieldType, + MetricFindValue, +} from '@grafana/data'; + +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { HaystackVariableQueryEditor } from './components/HaystackVariableQueryEditor'; +import { DataSource } from './datasource'; +import { isRef, parseRef } from 'haystack'; +import { HaystackVariableQuery } from './types'; + +export class HaystackVariableSupport extends CustomVariableSupport { + editor = HaystackVariableQueryEditor; + + constructor(private datasource: DataSource) { + super(); + } + + query(request: DataQueryRequest): Observable { + let variableQuery = request.targets[0]; + let observable = this.datasource.query(request); + return observable.pipe( + map((response) => { + if (response === undefined || response.errors !== undefined || response.data === undefined) { + return response; + } + + let variableValues = response.data.reduce((acc: MetricFindValue[], frame: DataFrame) => { + // Default to the first field + let column = frame.fields[0]; + if (variableQuery.column !== undefined && variableQuery.column !== '') { + // If a column was input, match the column name + column = frame.fields.find((field: Field) => field.name === variableQuery.column) ?? column; + } else if (frame.fields.some((field: Field) => field.name === 'id')) { + // If there is an id column, use that + column = frame.fields.find((field: Field) => field.name === 'id') ?? column; + } + + // Default to the selected column + let displayColumn = column; + if (variableQuery.displayColumn !== undefined && variableQuery.displayColumn !== '') { + // If a column was input, match the column name + displayColumn = + frame.fields.find((field: Field) => { + return field.name === variableQuery.displayColumn; + }) ?? displayColumn; + } + + let variableValues = column.values.map((value, index) => { + let variableValue = variableValueFromCell(value, column.type); + + let displayValue = displayColumn.values[index]; + let variableText = variableTextFromCell(displayValue, displayColumn.type); + + return { text: variableText, value: variableValue }; + }); + return acc.concat(variableValues); + }, []); + return { ...response, data: variableValues }; + }) + ); + } +} + +function variableValueFromCell(value: string, columnType: FieldType): string { + switch (columnType) { + case FieldType.string: + if (isRef(value)) { + return parseRef(value).id; + } + } + return value; +} + +function variableTextFromCell(value: string, columnType: FieldType): string { + switch (columnType) { + case FieldType.string: + if (isRef(value)) { + let ref = parseRef(value); + return ref.dis ?? ref.id; + } + } + return value; +} diff --git a/src/components/VariableQueryEditor.tsx b/src/components/HaystackVariableQueryEditor.tsx similarity index 79% rename from src/components/VariableQueryEditor.tsx rename to src/components/HaystackVariableQueryEditor.tsx index 080c6ff..1702e57 100644 --- a/src/components/VariableQueryEditor.tsx +++ b/src/components/HaystackVariableQueryEditor.tsx @@ -6,9 +6,11 @@ import { QueryEditorProps } from '@grafana/data'; import { InlineField, Input, Stack } from '@grafana/ui'; import { DataSource } from 'datasource'; -type Props = QueryEditorProps; +export type Props = QueryEditorProps; -export const VariableQueryEditor = ({ onChange, query }: Props) => { +const refId = 'HaystackVariableQueryEditor-VariableQuery'; + +export const HaystackVariableQueryEditor = ({ onChange, query }: Props) => { let variableInputWidth = 30; // Computes the query string and calls the onChange function. Should be used instead of onChange for all mutating functions. @@ -42,22 +44,28 @@ export const VariableQueryEditor = ({ onChange, query }: Props) => { const onQueryChange = (newQuery: string) => { if (query.type === "eval") { - onChangeAndSave({ ...query, eval: newQuery }); + onChangeAndSave({ ...query, refId: refId, eval: newQuery }); } else if (query.type === "hisRead") { - onChangeAndSave({ ...query, hisRead: newQuery }); + onChangeAndSave({ ...query, refId: refId, hisRead: newQuery }); } else if (query.type === "hisReadFilter") { - onChangeAndSave({ ...query, hisReadFilter: newQuery }); + onChangeAndSave({ ...query, refId: refId, hisReadFilter: newQuery }); } else if (query.type === "read") { - onChangeAndSave({ ...query, read: newQuery }); + onChangeAndSave({ ...query, refId: refId, read: newQuery }); } }; const onColumnChange = (event: React.FormEvent) => { - onChangeAndSave({...query, column: event.currentTarget.value,}); + onChangeAndSave({...query, column: event.currentTarget.value }); }; const onDisplayColumnChange = (event: React.FormEvent) => { - onChangeAndSave({...query, displayColumn: event.currentTarget.value,}); + onChangeAndSave({...query, displayColumn: event.currentTarget.value }); + }; + + const handleBlur = () => { + if (query.query !== undefined && query.query !== "") { + onChange({ ...query, refId: refId }); + } }; return ( @@ -78,6 +86,7 @@ export const VariableQueryEditor = ({ onChange, query }: Props) => { { { constructor(instanceSettings: DataSourceInstanceSettings) { super(instanceSettings); - this.variables = new HaystackVariableSupport((request) => { - return this.query(request) - }); + this.variables = new HaystackVariableSupport(this); } // Queries the available ops from the datasource and returns the queryTypes that are supported. @@ -115,82 +106,3 @@ export class DataSource extends DataSourceWithBackend { - editor: ComponentType>; - - // Requests data from the backend. This allows this class to reuse the DataSource.query method to get data. - onQuery: (request: DataQueryRequest) => Observable; - - constructor(onQuery: (request: DataQueryRequest) => Observable) { - super(); - this.editor = VariableQueryEditor; - this.onQuery = onQuery; - } - - query(request: DataQueryRequest): Observable { - let variableQuery = request.targets[0]; - let observable = this.onQuery(request); - return observable.pipe( - map((response) => { - if (response === undefined || response.errors !== undefined || response.data === undefined) { - return response - } - - let variableValues = response.data.reduce((acc: MetricFindValue[], frame: DataFrame) => { - // Default to the first field - let column = frame.fields[0]; - if (variableQuery.column !== undefined && variableQuery.column !== '') { - // If a column was input, match the column name - column = frame.fields.find((field: Field) => field.name === variableQuery.column) ?? column; - } else if (frame.fields.some((field: Field) => field.name === 'id')) { - // If there is an id column, use that - column = frame.fields.find((field: Field) => field.name === 'id') ?? column; - } - - // Default to the selected column - let displayColumn = column; - if (variableQuery.displayColumn !== undefined && variableQuery.displayColumn !== '') { - // If a column was input, match the column name - displayColumn = frame.fields.find((field: Field) => { - return field.name === variableQuery.displayColumn - }) ?? displayColumn; - } - - let variableValues = column.values.map((value, index) => { - let variableValue = variableValueFromCell(value, column.type); - - let displayValue = displayColumn.values[index]; - let variableText = variableTextFromCell(displayValue, displayColumn.type); - - return { text: variableText, value: variableValue }; - }); - return acc.concat(variableValues); - }, []); - return { ...response, data: variableValues }; - }) - ); - } -} - - -function variableValueFromCell(value: string, columnType: FieldType): string { - switch (columnType) { - case FieldType.string: - if (isRef(value)) { - return parseRef(value).id; - } - } - return value; -} - -function variableTextFromCell(value: string, columnType: FieldType): string { - switch (columnType) { - case FieldType.string: - if (isRef(value)) { - let ref = parseRef(value); - return ref.dis ?? ref.id; - } - } - return value; -}