Skip to content

Commit d2c0a35

Browse files
Merge pull request #41 from NeedleInAJayStack/feature/variable-display-column
Adds variable display column selection
2 parents ce411ba + c727682 commit d2c0a35

File tree

6 files changed

+100
-33
lines changed

6 files changed

+100
-33
lines changed

src/components/VariableQueryEditor.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface VariableQueryProps {
1212
const refId = "variable";
1313

1414
export const VariableQueryEditor: React.FC<VariableQueryProps> = ({ onChange, query: variableQuery }) => {
15+
let variableInputWidth = 30;
1516
const [query, setState] = useState(variableQuery);
1617

1718
const saveQuery = () => {
@@ -33,7 +34,11 @@ export const VariableQueryEditor: React.FC<VariableQueryProps> = ({ onChange, qu
3334
if (query.column !== undefined && query.column !== '') {
3435
column = `'${query.column}'`;
3536
}
36-
let displayString = `${type}: '${queryCmd}', Column: ${column}`
37+
let displayColumn = "none";
38+
if (query.displayColumn !== undefined && query.displayColumn !== '') {
39+
displayColumn = `'${query.displayColumn}'`;
40+
}
41+
let displayString = `${type}: '${queryCmd}', Column: ${column}, Display: ${displayColumn}`
3742
onChange(query, displayString);
3843
};
3944

@@ -57,6 +62,10 @@ export const VariableQueryEditor: React.FC<VariableQueryProps> = ({ onChange, qu
5762
setState({...query, column: event.currentTarget.value,});
5863
};
5964

65+
const onDisplayColumnChange = (event: React.FormEvent<HTMLInputElement>) => {
66+
setState({...query, displayColumn: event.currentTarget.value,});
67+
};
68+
6069
return (
6170
<div onBlur={saveQuery}>
6271
<HaystackQueryTypeSelector
@@ -71,8 +80,18 @@ export const VariableQueryEditor: React.FC<VariableQueryProps> = ({ onChange, qu
7180
/>
7281
<InlineField label="Column">
7382
<Input
83+
width={variableInputWidth}
7484
onChange={onColumnChange}
7585
value={query.column}
86+
placeholder="Defaults to 'id' or first column"
87+
/>
88+
</InlineField>
89+
<InlineField label="Display Column">
90+
<Input
91+
width={variableInputWidth}
92+
onChange={onDisplayColumnChange}
93+
value={query.displayColumn}
94+
placeholder="Defaults to 'Column'"
7695
/>
7796
</InlineField>
7897
</div>

src/datasource.ts

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';
1212

1313
import { HaystackQuery, OpsQuery, HaystackDataSourceOptions, HaystackVariableQuery, QueryType } from './types';
1414
import { firstValueFrom } from 'rxjs';
15+
import { isRef, parseRef } from 'haystack';
1516

1617
export const queryTypes: QueryType[] = [
1718
{ label: 'Eval', value: 'eval', apiRequirements: ['eval'], description: 'Evaluate an Axon expression' },
@@ -100,39 +101,33 @@ export class DataSource extends DataSourceWithBackend<HaystackQuery, HaystackDat
100101

101102
return response.data.reduce((acc: MetricFindValue[], frame: DataFrame) => {
102103
// Default to the first field
103-
let field = frame.fields[0];
104+
let column = frame.fields[0];
104105
if (variableQuery.column !== undefined && variableQuery.column !== '') {
105106
// If a column was input, match the column name
106-
field = frame.fields.find((field: Field) => field.name === variableQuery.column) ?? field;
107+
column = frame.fields.find((field: Field) => field.name === variableQuery.column) ?? column;
107108
} else if (frame.fields.some((field: Field) => field.name === 'id')) {
108109
// If there is an id column, use that
109-
field = frame.fields.find((field: Field) => field.name === 'id') ?? field;
110+
column = frame.fields.find((field: Field) => field.name === 'id') ?? column;
110111
}
111112

112-
let fieldVals = field.values.map((value) => {
113-
switch (field.type) {
114-
case FieldType.string:
115-
if (value.startsWith('@')) {
116-
// Detect ref using @ prefix, and adjust value to just the Ref
117-
const spaceIndex = value.indexOf(' ');
118-
if (spaceIndex > -1) {
119-
// Display name exists
120-
const id = value.substring(0, spaceIndex);
121-
const dis = value.substring(spaceIndex + 2, value.length - 1);
122-
return { text: dis, value: id };
123-
} else {
124-
// Otherwise, just use id
125-
return { text: value, value: value };
126-
}
127-
} else {
128-
// Otherwise, just use the value directly
129-
return { text: value, value: value };
130-
}
131-
default:
132-
return { text: value, value: value };
133-
}
113+
// Default to the selected column
114+
let displayColumn = column;
115+
if (variableQuery.displayColumn !== undefined && variableQuery.displayColumn !== '') {
116+
// If a column was input, match the column name
117+
displayColumn =
118+
frame.fields.find((field: Field) => field.name === variableQuery.displayColumn) ?? displayColumn;
119+
}
120+
121+
let variableValues = column.values.map((value, index) => {
122+
let variableValue = variableValueFromCell(value, column.type);
123+
124+
let displayValue = displayColumn.values[index];
125+
let variableText = variableTextFromCell(displayValue, displayColumn.type);
126+
127+
return { text: variableText, value: variableValue };
134128
});
135-
return acc.concat(fieldVals);
129+
130+
return acc.concat(variableValues);
136131
}, []);
137132
}
138133

@@ -154,3 +149,24 @@ export class DataSource extends DataSourceWithBackend<HaystackQuery, HaystackDat
154149
};
155150
}
156151
}
152+
153+
function variableValueFromCell(value: string, columnType: FieldType): string {
154+
switch (columnType) {
155+
case FieldType.string:
156+
if (isRef(value)) {
157+
return parseRef(value).id;
158+
}
159+
}
160+
return value;
161+
}
162+
163+
function variableTextFromCell(value: string, columnType: FieldType): string {
164+
switch (columnType) {
165+
case FieldType.string:
166+
if (isRef(value)) {
167+
let ref = parseRef(value);
168+
return ref.dis ?? ref.id;
169+
}
170+
}
171+
return value;
172+
}

src/haystack.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { isRef, parseRef } from 'haystack';
2+
3+
// Just a stub test
4+
describe('haystack test', () => {
5+
it('isRef', () => {
6+
expect(isRef('@abc')).toBe(true);
7+
expect(isRef('@abc 123')).toBe(true);
8+
expect(isRef('@abc 123 abc')).toBe(true);
9+
expect(isRef('abc 123')).toBe(false);
10+
expect(isRef('abc @123')).toBe(false);
11+
});
12+
13+
it('parseRef', () => {
14+
expect(parseRef('@abc')).toStrictEqual({ id: '@abc', dis: null });
15+
expect(parseRef('@abc "123"')).toStrictEqual({ id: '@abc', dis: '123' });
16+
expect(parseRef('@abc "123 abc"')).toStrictEqual({ id: '@abc', dis: '123 abc' });
17+
});
18+
});

src/haystack.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Detect refs using @ prefix
2+
export function isRef(value: string): boolean {
3+
return value.startsWith('@');
4+
}
5+
6+
// Converts a string into ref components by detecting the first space. Assumes format `@id "dis"`
7+
export function parseRef(value: string): { id: string; dis: string | null } {
8+
let id = value;
9+
let dis: string | null = null;
10+
const spaceIndex = value.indexOf(' ');
11+
12+
if (spaceIndex > -1) {
13+
// Display name exists
14+
id = value.substring(0, spaceIndex);
15+
// Cut off leading and trailing quotes
16+
dis = value.substring(spaceIndex + 2, value.length - 1);
17+
}
18+
return { id: id, dis: dis };
19+
}

src/module.test.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface QueryType extends SelectableValue<string> {
3030

3131
export interface HaystackVariableQuery extends HaystackQuery {
3232
column: string;
33+
displayColumn: string;
3334
refId: string;
3435
}
3536

0 commit comments

Comments
 (0)