Skip to content

Commit

Permalink
feat: add support for variables #5
Browse files Browse the repository at this point in the history
  • Loading branch information
Loori-R committed Feb 21, 2024
1 parent 215a2fa commit df66251
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Changelog

## tip

* FEATURE: add support for variables in the query. See [this issue](https://github.com/VictoriaMetrics/victorialogs-datasource/issues/5).
82 changes: 79 additions & 3 deletions src/datasource.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { map as lodashMap } from 'lodash';
import { Observable } from "rxjs";
import { map } from 'rxjs/operators';

import { DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings } from '@grafana/data';
import { DataSourceWithBackend } from '@grafana/runtime';
import {
AdHocVariableFilter,
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
ScopedVars
} from '@grafana/data';
import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';

import { transformBackendResult } from "./backendResultTransformer";
import QueryEditor from "./components/QueryEditor/QueryEditor";
import { escapeLabelValueInSelector } from "./languageUtils";
import { escapeLabelValueInSelector, isRegexSelector } from "./languageUtils";
import { addLabelToQuery, queryHasFilter, removeLabelFromQuery } from "./modifyQuery";
import { replaceVariables, returnVariables } from "./parsingUtils";
import { Query, Options, ToggleFilterAction, QueryFilterOptions, FilterActionType } from './types';

export class VictoriaLogsDatasource
Expand All @@ -16,6 +24,7 @@ export class VictoriaLogsDatasource

constructor(
instanceSettings: DataSourceInstanceSettings<Options>,
private readonly templateSrv: TemplateSrv = getTemplateSrv()
) {
super(instanceSettings);

Expand Down Expand Up @@ -82,4 +91,71 @@ export class VictoriaLogsDatasource
let expression = query.expr ?? '';
return queryHasFilter(expression, filter.key, filter.value);
}

applyTemplateVariables(target: Query, scopedVars: ScopedVars, adhocFilters?: AdHocVariableFilter[]): Query {
const { __auto, __interval, __interval_ms, __range, __range_s, __range_ms, ...rest } = scopedVars || {};
const exprWithAdHoc = this.addAdHocFilters(target.expr, adhocFilters);

const variables = {
...rest,
__interval: {
value: '$__interval',
},
__interval_ms: {
value: '$__interval_ms',
},
};
return {
...target,
legendFormat: this.templateSrv.replace(target.legendFormat, rest),
expr: this.templateSrv.replace(exprWithAdHoc, variables, this.interpolateQueryExpr),
};
}

addAdHocFilters(queryExpr: string, adhocFilters?: AdHocVariableFilter[]) {
if (!adhocFilters) {
return queryExpr;
}

let expr = replaceVariables(queryExpr);

expr = adhocFilters.reduce((acc: string, filter: { key: string; operator: string; value: string }) => {
const { key, operator } = filter;
let { value } = filter;
if (isRegexSelector(operator)) {
value = regularEscape(value);
} else {
value = escapeLabelValueInSelector(value, operator);
}
return addLabelToQuery(acc, key, operator, value);
}, expr);

return returnVariables(expr);
}

interpolateQueryExpr(value: any, variable: any) {
if (!variable.multi && !variable.includeAll) {
return regularEscape(value);
}

if (typeof value === 'string') {
return specialRegexEscape(value);
}

return lodashMap(value, specialRegexEscape).join('|');
}
}

export function regularEscape(value: any) {
if (typeof value === 'string') {
return value.replace(/'/g, "\\\\'");
}
return value;
}

export function specialRegexEscape(value: any) {
if (typeof value === 'string') {
return regularEscape(value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]+?.()|]/g, '\\\\$&'));
}
return value;
}
33 changes: 33 additions & 0 deletions src/parsingUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const varTypeFunc = [
(v: string) => `\$${v}`,
(v: string, f?: string) => `[[${v}${f ? `:${f}` : ''}]]`,
(v: string, f?: string) => `\$\{${v}${f ? `:${f}` : ''}\}`,
];

export const variableRegex = /\$(\w+)|\[\[([\s\S]+?)(?::(\w+))?]]|\${(\w+)(?:\.([^:^}]+))?(?::([^}]+))?}/g;

export function returnVariables(expr: string) {
const replacer = (match: string, type: any, v: any, f: any) => varTypeFunc[parseInt(type, 10)](v, f)
return expr.replace(/__V_(\d)__(.+?)__V__(?:__F__(\w+)__F__)?/g, replacer);
}


export function replaceVariables(expr: string) {
return expr.replace(variableRegex, (match, var1, var2, fmt2, var3, fieldPath, fmt3) => {
const fmt = fmt2 || fmt3;
let variable = var1;
let varType = '0';

if (var2) {
variable = var2;
varType = '1';
}

if (var3) {
variable = var3;
varType = '2';
}

return `__V_${varType}__` + variable + '__V__' + (fmt ? '__F__' + fmt + '__F__' : '');
});
}

0 comments on commit df66251

Please sign in to comment.