Graphite: Backend tag values autocomplete endpoint (#110773)
* Add lint rules * Backend decoupling - Add standalone files - Add graphite query type - Add logger to Service - Create logger in the ProvideService method - Use a pointer for the HTTP client provider - Update logger usage everywhere - Update tracer type - Replace simplejson with json - Add dummy CallResource and CheckHealth methods - Update tests * Update ConfigEditor imports * Update types imports * Update datasource - Switch to using semver package - Update imports * Update store imports * Update helper imports and notification creation * Update context import * Update version numbers and logic * Copy array_move from core * Test updates * Add required files and update plugin.json * Update core references and packages * Remove commented code * Update wire * Lint * Fix import * Copy null type * More lint * Update snapshot * Refactor backend - Split query logic into separate file - Move utils to separate file * Add health-check logic - Support backend healthcheck if the FF is enabled * Remove query import support as unneeded * Add test * Add util function for decoding responses * Add events types * Add resource handler * Add events handler and generic resource req handler * Tests * Update frontend - Add types - Update events function to support backend requests * Lint and typing * Lint * Add metrics find endpoint - Add types - Add generic response parser - Add endpoint - Tests * Update FE functoin to use backend endpoint * Lint * Simplify request * Update test * Metrics expand type * Extract shared logic and add metric expand endpoint * Update tests * Call metric expand from backend * Rename type for clarity * Add get resource req handler * Refactor doGraphiteRequest, parseResponse Update tests * Migrate functions endpoint to backend * Support tags autocomplete in backend - Add tests - Add types - Remove unneeded comments * Support tag values autocomplete - Remove unused frontend endpoints - Add types - Update tests * Add tests * Review * Review * Fix packages * Format * Fix merge issues * Review * Fix undefined values * Extract request creation - Add method for create requests generically with tests - Replace usage in query method - Update usages in resource handlers - Update tests - Update types * Lint
This commit is contained in:
@@ -4108,7 +4108,7 @@
|
||||
"count": 4
|
||||
},
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 10
|
||||
"count": 8
|
||||
}
|
||||
},
|
||||
"public/app/plugins/datasource/graphite/gfunc.ts": {
|
||||
|
||||
@@ -109,8 +109,10 @@ func (s *Service) createRequest(ctx context.Context, dsInfo *datasourceInfo, par
|
||||
|
||||
if params.QueryParams != nil {
|
||||
queryValues := u.Query()
|
||||
for k, v := range params.QueryParams {
|
||||
queryValues.Set(k, v)
|
||||
for key, values := range params.QueryParams {
|
||||
for _, value := range values {
|
||||
queryValues.Add(key, value)
|
||||
}
|
||||
}
|
||||
u.RawQuery = queryValues.Encode()
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func Test_CreateRequest(t *testing.T) {
|
||||
expectedMethod string
|
||||
expectedError string
|
||||
checkHeaders map[string]string
|
||||
checkQuery map[string]string
|
||||
checkQuery map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "basic request with default GET method",
|
||||
@@ -57,16 +57,16 @@ func Test_CreateRequest(t *testing.T) {
|
||||
name: "request with query parameters",
|
||||
dsInfo: dsInfo,
|
||||
params: URLParams{
|
||||
QueryParams: map[string]string{
|
||||
"query": "stats.counters.*",
|
||||
"format": "json",
|
||||
QueryParams: map[string][]string{
|
||||
"query": {"stats.counters.*"},
|
||||
"format": {"json"},
|
||||
},
|
||||
},
|
||||
expectedURL: "http://graphite.example.com",
|
||||
expectedMethod: "GET",
|
||||
checkQuery: map[string]string{
|
||||
"query": "stats.counters.*",
|
||||
"format": "json",
|
||||
checkQuery: map[string][]string{
|
||||
"query": {"stats.counters.*"},
|
||||
"format": {"json"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -99,9 +99,9 @@ func Test_CreateRequest(t *testing.T) {
|
||||
params: URLParams{
|
||||
SubPath: "/metrics/expand",
|
||||
Method: "POST",
|
||||
QueryParams: map[string]string{
|
||||
"groupByExpr": "true",
|
||||
"leavesOnly": "false",
|
||||
QueryParams: map[string][]string{
|
||||
"groupByExpr": {"true"},
|
||||
"leavesOnly": {"false"},
|
||||
},
|
||||
Headers: map[string]string{
|
||||
"X-Custom-Header": "test-value",
|
||||
@@ -110,9 +110,9 @@ func Test_CreateRequest(t *testing.T) {
|
||||
},
|
||||
expectedURL: "http://graphite.example.com/metrics/expand",
|
||||
expectedMethod: "POST",
|
||||
checkQuery: map[string]string{
|
||||
"groupByExpr": "true",
|
||||
"leavesOnly": "false",
|
||||
checkQuery: map[string][]string{
|
||||
"groupByExpr": {"true"},
|
||||
"leavesOnly": {"false"},
|
||||
},
|
||||
checkHeaders: map[string]string{
|
||||
"X-Custom-Header": "test-value",
|
||||
@@ -130,16 +130,30 @@ func Test_CreateRequest(t *testing.T) {
|
||||
name: "empty query parameter values",
|
||||
dsInfo: dsInfo,
|
||||
params: URLParams{
|
||||
QueryParams: map[string]string{
|
||||
"empty": "",
|
||||
"valid": "value",
|
||||
QueryParams: map[string][]string{
|
||||
"empty": {""},
|
||||
"valid": {"value"},
|
||||
},
|
||||
},
|
||||
expectedURL: "http://graphite.example.com",
|
||||
expectedMethod: "GET",
|
||||
checkQuery: map[string]string{
|
||||
"empty": "",
|
||||
"valid": "value",
|
||||
checkQuery: map[string][]string{
|
||||
"empty": {""},
|
||||
"valid": {"value"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi-valued query parameter",
|
||||
dsInfo: dsInfo,
|
||||
params: URLParams{
|
||||
QueryParams: map[string][]string{
|
||||
"valid": {"value1", "value2"},
|
||||
},
|
||||
},
|
||||
expectedURL: "http://graphite.example.com",
|
||||
expectedMethod: "GET",
|
||||
checkQuery: map[string][]string{
|
||||
"valid": {"value1", "value2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -163,9 +177,13 @@ func Test_CreateRequest(t *testing.T) {
|
||||
assert.Equal(t, tt.expectedMethod, req.Method)
|
||||
|
||||
if tt.checkQuery != nil {
|
||||
for key, expectedValue := range tt.checkQuery {
|
||||
actualValue := req.URL.Query().Get(key)
|
||||
assert.Equal(t, expectedValue, actualValue, "Query parameter %s", key)
|
||||
for key, expectedValues := range tt.checkQuery {
|
||||
actualValue := req.URL.Query()[key]
|
||||
assert.NotZero(t, len(actualValue))
|
||||
|
||||
for _, expectedValue := range expectedValues {
|
||||
assert.Contains(t, actualValue, expectedValue, "Query parameter %s", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ func (s *Service) newResourceMux() *http.ServeMux {
|
||||
mux.HandleFunc("/metrics/expand", handleResourceReq(s.handleMetricsExpand, s))
|
||||
mux.HandleFunc("/functions", handleResourceReq(s.handleFunctions, s))
|
||||
mux.HandleFunc("/tags/autoComplete/tags", handleResourceReq(s.handleTagsAutocomplete, s))
|
||||
mux.HandleFunc("/tags/autoComplete/values", handleResourceReq(s.handleTagValuesAutocomplete, s))
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
@@ -87,12 +89,12 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw
|
||||
}
|
||||
|
||||
func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, eventsRequestJson *GraphiteEventsRequest) ([]byte, int, error) {
|
||||
queryParams := map[string]string{
|
||||
"from": eventsRequestJson.From,
|
||||
"until": eventsRequestJson.Until,
|
||||
queryParams := map[string][]string{
|
||||
"from": {eventsRequestJson.From},
|
||||
"until": {eventsRequestJson.Until},
|
||||
}
|
||||
if eventsRequestJson.Tags != "" {
|
||||
queryParams["tags"] = eventsRequestJson.Tags
|
||||
queryParams["tags"] = []string{eventsRequestJson.Tags}
|
||||
}
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
@@ -128,12 +130,12 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo,
|
||||
data := url.Values{}
|
||||
data.Set("query", metricsFindRequestJson.Query)
|
||||
|
||||
queryParams := map[string]string{}
|
||||
queryParams := map[string][]string{}
|
||||
if metricsFindRequestJson.From != "" {
|
||||
queryParams["from"] = metricsFindRequestJson.From
|
||||
queryParams["from"] = []string{metricsFindRequestJson.From}
|
||||
}
|
||||
if metricsFindRequestJson.Until != "" {
|
||||
queryParams["until"] = metricsFindRequestJson.Until
|
||||
queryParams["until"] = []string{metricsFindRequestJson.Until}
|
||||
}
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
@@ -165,14 +167,14 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf
|
||||
return nil, http.StatusBadRequest, fmt.Errorf("query is required")
|
||||
}
|
||||
|
||||
queryParams := map[string]string{
|
||||
"query": metricsExpandRequestJson.Query,
|
||||
queryParams := map[string][]string{
|
||||
"query": {metricsExpandRequestJson.Query},
|
||||
}
|
||||
if metricsExpandRequestJson.From != "" {
|
||||
queryParams["from"] = metricsExpandRequestJson.From
|
||||
queryParams["from"] = []string{metricsExpandRequestJson.From}
|
||||
}
|
||||
if metricsExpandRequestJson.Until != "" {
|
||||
queryParams["until"] = metricsExpandRequestJson.Until
|
||||
queryParams["until"] = []string{metricsExpandRequestJson.Until}
|
||||
}
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
@@ -205,11 +207,11 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf
|
||||
}
|
||||
|
||||
func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasourceInfo, tagsAutocompleteRequestJson *GraphiteTagsRequest) ([]byte, int, error) {
|
||||
queryParams := map[string]string{
|
||||
"from": tagsAutocompleteRequestJson.From,
|
||||
"until": tagsAutocompleteRequestJson.Until,
|
||||
"limit": fmt.Sprintf("%d", tagsAutocompleteRequestJson.Limit),
|
||||
"tagPrefix": tagsAutocompleteRequestJson.TagPrefix,
|
||||
queryParams := map[string][]string{
|
||||
"from": {tagsAutocompleteRequestJson.From},
|
||||
"until": {tagsAutocompleteRequestJson.Until},
|
||||
"limit": {fmt.Sprintf("%d", tagsAutocompleteRequestJson.Limit)},
|
||||
"tagPrefix": {tagsAutocompleteRequestJson.TagPrefix},
|
||||
}
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "tags/autoComplete/tags",
|
||||
@@ -217,7 +219,7 @@ func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasource
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics expand request %v", err)
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create tags autocomplete request %v", err)
|
||||
}
|
||||
|
||||
tags, _, statusCode, err := doGraphiteRequest[[]string](ctx, dsInfo, s.logger, req, false)
|
||||
@@ -233,6 +235,38 @@ func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasource
|
||||
return tagsResponse, statusCode, nil
|
||||
}
|
||||
|
||||
func (s *Service) handleTagValuesAutocomplete(ctx context.Context, dsInfo *datasourceInfo, tagValuesAutocompleteRequestJson *GraphiteTagValuesRequest) ([]byte, int, error) {
|
||||
queryParams := map[string][]string{
|
||||
"expr": tagValuesAutocompleteRequestJson.Expr,
|
||||
"tag": {tagValuesAutocompleteRequestJson.Tag},
|
||||
"from": {tagValuesAutocompleteRequestJson.From},
|
||||
"until": {tagValuesAutocompleteRequestJson.Until},
|
||||
"limit": {fmt.Sprintf("%d", tagValuesAutocompleteRequestJson.Limit)},
|
||||
"valuePrefix": {tagValuesAutocompleteRequestJson.ValuePrefix},
|
||||
}
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "tags/autoComplete/values",
|
||||
Method: http.MethodGet,
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create tag values autocomplete request %v", err)
|
||||
}
|
||||
|
||||
tagValues, _, statusCode, err := doGraphiteRequest[[]string](ctx, dsInfo, s.logger, req, false)
|
||||
if err != nil {
|
||||
return nil, statusCode, fmt.Errorf("tag values autocomplete request failed: %v", err)
|
||||
}
|
||||
|
||||
tagValuesResponse, err := json.Marshal(tagValues)
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal tag values autocomplete response: %s", err)
|
||||
}
|
||||
|
||||
return tagValuesResponse, statusCode, nil
|
||||
}
|
||||
|
||||
func (s *Service) handleFunctions(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) {
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "functions",
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
@@ -498,6 +499,127 @@ func TestHandleTagsAutocomplete(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleTagValuesAutocomplete(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
request GraphiteTagValuesRequest
|
||||
responseBody string
|
||||
statusCode int
|
||||
expectError bool
|
||||
errorContains string
|
||||
expectedData []string
|
||||
}{
|
||||
{
|
||||
name: "successful tag values autocomplete request",
|
||||
request: GraphiteTagValuesRequest{
|
||||
Expr: []string{"app=*"},
|
||||
Tag: "environment",
|
||||
From: "1h",
|
||||
Until: "now",
|
||||
Limit: 5,
|
||||
ValuePrefix: "prod",
|
||||
},
|
||||
responseBody: `["production", "prod-eu", "prod-us"]`,
|
||||
statusCode: 200,
|
||||
expectedData: []string{"production", "prod-eu", "prod-us"},
|
||||
},
|
||||
{
|
||||
name: "multiple expressions",
|
||||
request: GraphiteTagValuesRequest{
|
||||
Expr: []string{"app=*", "region=us-*"},
|
||||
Tag: "environment",
|
||||
From: "1h",
|
||||
Until: "now",
|
||||
Limit: 5,
|
||||
ValuePrefix: "prod",
|
||||
},
|
||||
responseBody: `["production", "prod-eu", "prod-us"]`,
|
||||
statusCode: 200,
|
||||
expectedData: []string{"production", "prod-eu", "prod-us"},
|
||||
},
|
||||
{
|
||||
name: "tag values autocomplete with empty response",
|
||||
request: GraphiteTagValuesRequest{
|
||||
Expr: []string{"app=nonexistent"},
|
||||
Tag: "environment",
|
||||
ValuePrefix: "staging",
|
||||
},
|
||||
responseBody: `[]`,
|
||||
statusCode: 200,
|
||||
expectedData: []string{},
|
||||
},
|
||||
{
|
||||
name: "tag values autocomplete server error",
|
||||
request: GraphiteTagValuesRequest{
|
||||
Expr: []string{"invalid-expr"},
|
||||
Tag: "env",
|
||||
},
|
||||
responseBody: `invalid json response`,
|
||||
statusCode: 400,
|
||||
expectError: true,
|
||||
errorContains: "tag values autocomplete request failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockTransport := &mockRoundTripper{
|
||||
respBody: []byte(tt.responseBody),
|
||||
status: tt.statusCode,
|
||||
}
|
||||
|
||||
dsInfo := &datasourceInfo{
|
||||
HTTPClient: &http.Client{Transport: mockTransport},
|
||||
URL: "http://graphite.example.com",
|
||||
}
|
||||
|
||||
service := &Service{
|
||||
logger: log.NewNullLogger(),
|
||||
}
|
||||
|
||||
result, statusCode, err := service.handleTagValuesAutocomplete(context.Background(), dsInfo, &tt.request)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
if tt.errorContains != "" {
|
||||
assert.Contains(t, err.Error(), tt.errorContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.statusCode, statusCode)
|
||||
|
||||
var tagValues []string
|
||||
err = json.Unmarshal(result, &tagValues)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedData, tagValues)
|
||||
}
|
||||
|
||||
if !tt.expectError {
|
||||
expectedURL := "http://graphite.example.com/tags/autoComplete/values"
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.String(), expectedURL)
|
||||
|
||||
for _, expr := range tt.request.Expr {
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("expr=%s", url.QueryEscape(expr)))
|
||||
}
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("tag=%s", tt.request.Tag))
|
||||
|
||||
if tt.request.From != "" {
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("from=%s", tt.request.From))
|
||||
}
|
||||
if tt.request.Until != "" {
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("until=%s", tt.request.Until))
|
||||
}
|
||||
if tt.request.Limit != 0 {
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("limit=%d", tt.request.Limit))
|
||||
}
|
||||
if tt.request.ValuePrefix != "" {
|
||||
assert.Contains(t, mockTransport.lastRequest.URL.RawQuery, fmt.Sprintf("valuePrefix=%s", tt.request.ValuePrefix))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestHandleFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -16,7 +16,7 @@ type URLParams struct {
|
||||
SubPath string
|
||||
Method string
|
||||
Body io.Reader
|
||||
QueryParams map[string]string
|
||||
QueryParams map[string][]string
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
@@ -66,3 +66,12 @@ type GraphiteTagsRequest struct {
|
||||
Limit int `json:"limit,omitempty"`
|
||||
TagPrefix string `json:"tagPrefix,omitempty"`
|
||||
}
|
||||
|
||||
type GraphiteTagValuesRequest struct {
|
||||
Expr []string `json:"expr"`
|
||||
Tag string `json:"tag"`
|
||||
From string `json:"from"`
|
||||
Until string `json:"until"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
ValuePrefix string `json:"valuePrefix,omitempty"`
|
||||
}
|
||||
|
||||
@@ -789,71 +789,6 @@ export class GraphiteDatasource
|
||||
);
|
||||
}
|
||||
|
||||
getTags(optionalOptions: any) {
|
||||
const options = optionalOptions || {};
|
||||
const params: BackendSrvRequest['params'] = {};
|
||||
|
||||
if (options.range) {
|
||||
params.from = this.translateTime(options.range.from, false, options.timezone);
|
||||
params.until = this.translateTime(options.range.to, true, options.timezone);
|
||||
}
|
||||
|
||||
const httpOptions: BackendSrvRequest = {
|
||||
method: 'GET',
|
||||
url: '/tags',
|
||||
// for cancellations
|
||||
requestId: options.requestId,
|
||||
params,
|
||||
};
|
||||
|
||||
return lastValueFrom(
|
||||
this.doGraphiteRequest(httpOptions).pipe(
|
||||
map((results: FetchResponse) => {
|
||||
return _map(results.data, (tag) => {
|
||||
return {
|
||||
text: tag.tag,
|
||||
id: tag.id,
|
||||
};
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getTagValues(options: any = {}) {
|
||||
const params: BackendSrvRequest['params'] = {};
|
||||
|
||||
if (options.range) {
|
||||
params.from = this.translateTime(options.range.from, false, options.timezone);
|
||||
params.until = this.translateTime(options.range.to, true, options.timezone);
|
||||
}
|
||||
|
||||
const httpOptions: BackendSrvRequest = {
|
||||
method: 'GET',
|
||||
url: '/tags/' + this.templateSrv.replace(options.key),
|
||||
// for cancellations
|
||||
requestId: options.requestId,
|
||||
params,
|
||||
};
|
||||
|
||||
return lastValueFrom(
|
||||
this.doGraphiteRequest(httpOptions).pipe(
|
||||
map((results: FetchResponse) => {
|
||||
if (results.data && results.data.values) {
|
||||
return _map(results.data.values, (value) => {
|
||||
return {
|
||||
text: value.value,
|
||||
id: value.id,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async getTagsAutoComplete(expressions: string[], tagPrefix?: string, optionalOptions?: any) {
|
||||
const options = optionalOptions || {};
|
||||
const params: BackendSrvRequest['params'] = {
|
||||
@@ -894,7 +829,7 @@ export class GraphiteDatasource
|
||||
return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags()));
|
||||
}
|
||||
|
||||
getTagValuesAutoComplete(expressions: string[], tag: string, valuePrefix?: string, optionalOptions?: any) {
|
||||
async getTagValuesAutoComplete(expressions: string[], tag: string, valuePrefix?: string, optionalOptions?: any) {
|
||||
const options = optionalOptions || {};
|
||||
const params: BackendSrvRequest['params'] = {
|
||||
expr: _map(expressions, (expression) => this.templateSrv.replace((expression || '').trim())),
|
||||
@@ -911,6 +846,20 @@ export class GraphiteDatasource
|
||||
params.until = this.translateTime(options.range.to, true, options.timezone);
|
||||
}
|
||||
|
||||
if (config.featureToggles.graphiteBackendMode) {
|
||||
const tagValues = await this.postResource<string[]>('tags/autoComplete/values', {
|
||||
from: typeof params.from === 'string' ? params.from : `${params.from}`,
|
||||
until: typeof params.until === 'string' ? params.until : `${params.until}`,
|
||||
expr: params.expr,
|
||||
tag: params.tag,
|
||||
valuePrefix,
|
||||
limit: options.limit,
|
||||
});
|
||||
return tagValues.map((tag) => ({
|
||||
text: tag,
|
||||
}));
|
||||
}
|
||||
|
||||
const httpOptions: BackendSrvRequest = {
|
||||
method: 'GET',
|
||||
url: '/tags/autoComplete/values',
|
||||
|
||||
Reference in New Issue
Block a user