Skip to content

Commit

Permalink
Chained variables (#60)
Browse files Browse the repository at this point in the history
* Tinkering with chained

* Deal with missing imports

* Allow for custom values (operation is not being returned)

* Add support for scopedVars that override template variables

* Updated signature and tests
  • Loading branch information
heidmotron authored Sep 10, 2024
1 parent 01bcb3c commit 138ff49
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 22 deletions.
62 changes: 42 additions & 20 deletions src/components/VariableEditor/VariableEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useRef } from 'react';
import { AsyncSelect, InlineField } from '@grafana/ui';
import { AsyncSelect, Field, InlineField, TextArea } from '@grafana/ui';
import { DataQuery } from '@grafana/schema';
import { getBackendSrv } from '@grafana/runtime';
import {
Expand All @@ -10,8 +10,9 @@ import {
QueryEditorProps,
SelectableValue,
} from '@grafana/data';
import { Observable } from 'rxjs';
import { Observable} from 'rxjs';
import invariant from 'tiny-invariant';
import { createRequestVariables } from 'datasource';

/**
* The _Variables_ DataSource is defined separately from the UQL query
Expand All @@ -25,6 +26,7 @@ import invariant from 'tiny-invariant';
type VariableDataSource = DataSourceApi<VariableQuery>;
interface VariableQuery extends DataQuery {
attributeKey: string;
scopeFilterExpression?: string;
}

export class VariableEditor extends CustomVariableSupport<VariableDataSource> {
Expand All @@ -38,7 +40,7 @@ export class VariableEditor extends CustomVariableSupport<VariableDataSource> {
*/
query = (request: DataQueryRequest<VariableQuery>): Observable<DataQueryResponse> => {
const { url, projectName } = this;
const { attributeKey } = request.targets[0];
const { attributeKey, scopeFilterExpression } = request.targets[0];
invariant(typeof attributeKey === 'string', 'Invalid attribute key');

return new Observable((subscriber) => {
Expand All @@ -50,6 +52,8 @@ export class VariableEditor extends CustomVariableSupport<VariableDataSource> {
'scope-to-attribute-keys': [attributeKey],
'oldest-time': request.range.from,
'youngest-time': request.range.to,
'template-variables': createRequestVariables(),
'scope-to-filter-expression': scopeFilterExpression
},
})
.then((res: AttributeRes) => {
Expand Down Expand Up @@ -112,23 +116,41 @@ export class VariableEditor extends CustomVariableSupport<VariableDataSource> {
);

return (
<div className="gf-form">
<InlineField
label="Attribute key"
tooltip="Cloud Observability uses this key to populate the selectable values for the variable when viewing the dashboard. Choose from any attributes currently on your logs, metics, or traces."
>
<AsyncSelect
defaultOptions
cacheOptions
defaultValue={query ? { label: query.attributeKey, value: query.attributeKey } : undefined}
loadOptions={loadOptions}
onChange={(v) => {
if (v.value) {
onChange({ refId: v.value, attributeKey: v.value });
}
}}
/>
</InlineField>
<div>
<div className="gf-form">
<InlineField
label="Attribute key"
tooltip="Cloud Observability uses this key to populate the selectable values for the variable when viewing the dashboard. Choose from any attributes currently on your logs, metics, or traces."
>
<AsyncSelect
defaultOptions
cacheOptions
allowCustomValue={true}
defaultValue={query ? { label: query.attributeKey, value: query.attributeKey } : undefined}
loadOptions={loadOptions}
onChange={(v) => {
if (v.value) {
onChange({...query, refId: v.value, attributeKey: v.value });
}
}}
/>
</InlineField>
</div>

<div>
<Field label="UQL Filter Expression">
<TextArea
type="text"
width="100px"
rows={2}
defaultValue={query.scopeFilterExpression}
onChange={(ev) => {
if (ev.currentTarget.value) {
onChange({...query, scopeFilterExpression: ev.currentTarget.value})
}
}} />
</Field>
</div>
</div>
);
};
Expand Down
16 changes: 14 additions & 2 deletions src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DataSourceApi,
DataSourceInstanceSettings,
rangeUtil,
ScopedVars
} from '@grafana/data';
import { config, getBackendSrv, getTemplateSrv } from '@grafana/runtime';
import { preprocessData } from './preprocessors';
Expand Down Expand Up @@ -61,6 +62,7 @@ export class DataSource extends DataSourceApi<LightstepQuery, LightstepDataSourc
const notebookURL = createNotebookURL(request, visibleTargets, projectName);

const requests = visibleTargets.map(async (query) => {

const res = await getBackendSrv().post(`${this.url}/projects/${query.projectName}/telemetry/query_timeseries`, {
data: {
attributes: {
Expand All @@ -70,7 +72,7 @@ export class DataSource extends DataSourceApi<LightstepQuery, LightstepDataSourc
'youngest-time': request.range.to,
// query_timeseries minimum supported output-period is 1 second
'output-period': Math.max(1, rangeUtil.intervalToSeconds(request.interval)),
'template-variables': createRequestVariables(),
'template-variables': createRequestVariables(request.scopedVars),
},
analytics: {
anonymized_user: hashedEmail,
Expand Down Expand Up @@ -150,7 +152,7 @@ export class DataSource extends DataSourceApi<LightstepQuery, LightstepDataSourc
* Translates Grafana dashboard variables into a set of LS API template
* variables
*/
function createRequestVariables() {
export function createRequestVariables(scopedVars?: ScopedVars) {
return getTemplateSrv()
.getVariables()
.map((v) => {
Expand All @@ -163,6 +165,16 @@ function createRequestVariables() {
values = [];
}

// check if there are scopedVars in the request
// that will override the template variable
// nb: Panel options like Repeat will set scopedVars based on the selected template variable
if (scopedVars && scopedVars?.[v.name]) {
const scopedVar = scopedVars[v.name]?.text ?? "";
if (scopedVar !== "") {
values = [scopedVar]
}
}

return {
name: v.name,
values,
Expand Down

0 comments on commit 138ff49

Please sign in to comment.