mirror of https://github.com/grafana/grafana.git
Graphite: Backend functions endpoint (#110771)
* 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 * 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 * Lint
This commit is contained in:
parent
cb7abbaa0f
commit
3081ac166a
|
@ -1,6 +1,7 @@
|
|||
package graphite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -16,13 +17,14 @@ import (
|
|||
"go.opentelemetry.io/otel/codes"
|
||||
)
|
||||
|
||||
type resourceHandler[T any] func(context.Context, *datasourceInfo, T) ([]byte, int, error)
|
||||
type resourceHandler[T any] func(context.Context, *datasourceInfo, *T) ([]byte, int, error)
|
||||
|
||||
func (s *Service) newResourceMux() *http.ServeMux {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/events", handleResourceReq[GraphiteEventsRequest](s.handleEvents, s))
|
||||
mux.HandleFunc("/metrics/find", handleResourceReq[GraphiteMetricsFindRequest](s.handleMetricsFind, s))
|
||||
mux.HandleFunc("/metrics/expand", handleResourceReq[GraphiteMetricsFindRequest](s.handleMetricsExpand, s))
|
||||
mux.HandleFunc("/events", handleResourceReq(s.handleEvents, s))
|
||||
mux.HandleFunc("/metrics/find", handleResourceReq(s.handleMetricsFind, s))
|
||||
mux.HandleFunc("/metrics/expand", handleResourceReq(s.handleMetricsExpand, s))
|
||||
mux.HandleFunc("/functions", handleResourceReq(s.handleFunctions, s))
|
||||
return mux
|
||||
}
|
||||
|
||||
|
@ -39,17 +41,28 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw
|
|||
}
|
||||
|
||||
defer func() {
|
||||
if err := req.Body.Close(); err != nil {
|
||||
s.logger.Warn("Failed to close response body", "err", err)
|
||||
if req.Body != nil {
|
||||
if err := req.Body.Close(); err != nil {
|
||||
s.logger.Warn("Failed to close request body", "err", err)
|
||||
writeErrorResponse(rw, http.StatusInternalServerError, fmt.Sprintf("unexpected error %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var parsedBody *T
|
||||
if req.Body != nil {
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to read request body", "error", err)
|
||||
writeErrorResponse(rw, http.StatusInternalServerError, fmt.Sprintf("unexpected error %v", err))
|
||||
return
|
||||
}
|
||||
}()
|
||||
requestBody, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to read request body", "error", err)
|
||||
writeErrorResponse(rw, http.StatusInternalServerError, fmt.Sprintf("unexpected error %v", err))
|
||||
return
|
||||
parsedBody, err = parseRequestBody[T](body, s.logger)
|
||||
if err != nil {
|
||||
writeErrorResponse(rw, http.StatusBadRequest, fmt.Sprintf("failed to parse request body: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if handlerFn == nil {
|
||||
|
@ -57,13 +70,7 @@ func handleResourceReq[T any](handlerFn resourceHandler[T], s *Service) func(rw
|
|||
return
|
||||
}
|
||||
|
||||
parsedBody, err := parseRequestBody[T](requestBody, s.logger)
|
||||
if err != nil {
|
||||
writeErrorResponse(rw, http.StatusBadRequest, fmt.Sprintf("failed to parse request body: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
response, statusCode, err := handlerFn(ctx, dsInfo, *parsedBody)
|
||||
response, statusCode, err := handlerFn(ctx, dsInfo, parsedBody)
|
||||
if err != nil {
|
||||
writeErrorResponse(rw, statusCode, fmt.Sprintf("failed to handle resource request: %v", err))
|
||||
return
|
||||
|
@ -78,7 +85,7 @@ 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) {
|
||||
func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, eventsRequestJson *GraphiteEventsRequest) ([]byte, int, error) {
|
||||
queryParams := map[string]string{
|
||||
"from": eventsRequestJson.From,
|
||||
"until": eventsRequestJson.Until,
|
||||
|
@ -96,7 +103,7 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, even
|
|||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create events request %v", err)
|
||||
}
|
||||
|
||||
events, statusCode, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, dsInfo, s.logger, req)
|
||||
events, _, statusCode, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, dsInfo, s.logger, req, false)
|
||||
if err != nil {
|
||||
return nil, statusCode, fmt.Errorf("events request failed: %v", err)
|
||||
}
|
||||
|
@ -112,7 +119,7 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, even
|
|||
return graphiteEventsResponse, statusCode, nil
|
||||
}
|
||||
|
||||
func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, metricsFindRequestJson GraphiteMetricsFindRequest) ([]byte, int, error) {
|
||||
func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo, metricsFindRequestJson *GraphiteMetricsFindRequest) ([]byte, int, error) {
|
||||
if metricsFindRequestJson.Query == "" {
|
||||
return nil, http.StatusBadRequest, fmt.Errorf("query is required")
|
||||
}
|
||||
|
@ -139,7 +146,7 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo,
|
|||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics find request %v", err)
|
||||
}
|
||||
|
||||
metrics, statusCode, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, s.logger, req)
|
||||
metrics, _, statusCode, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, s.logger, req, false)
|
||||
if err != nil {
|
||||
return nil, statusCode, fmt.Errorf("metrics find request failed: %v", err)
|
||||
}
|
||||
|
@ -152,7 +159,7 @@ func (s *Service) handleMetricsFind(ctx context.Context, dsInfo *datasourceInfo,
|
|||
return metricsFindResponse, statusCode, nil
|
||||
}
|
||||
|
||||
func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInfo, metricsExpandRequestJson GraphiteMetricsFindRequest) ([]byte, int, error) {
|
||||
func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInfo, metricsExpandRequestJson *GraphiteMetricsFindRequest) ([]byte, int, error) {
|
||||
if metricsExpandRequestJson.Query == "" {
|
||||
return nil, http.StatusBadRequest, fmt.Errorf("query is required")
|
||||
}
|
||||
|
@ -176,7 +183,7 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf
|
|||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create metrics expand request %v", err)
|
||||
}
|
||||
|
||||
metrics, statusCode, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, s.logger, req)
|
||||
metrics, _, statusCode, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, s.logger, req, false)
|
||||
if err != nil {
|
||||
return nil, statusCode, fmt.Errorf("metrics expand request failed: %v", err)
|
||||
}
|
||||
|
@ -196,7 +203,29 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf
|
|||
return metricsExpandResponse, statusCode, nil
|
||||
}
|
||||
|
||||
func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logger log.Logger, req *http.Request) (*T, int, error) {
|
||||
func (s *Service) handleFunctions(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) {
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "functions",
|
||||
Method: http.MethodGet,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create functions request %v", err)
|
||||
}
|
||||
|
||||
_, rawBody, statusCode, err := doGraphiteRequest[map[string]any](ctx, dsInfo, s.logger, req, true)
|
||||
if err != nil {
|
||||
return nil, statusCode, fmt.Errorf("version request failed: %v", err)
|
||||
}
|
||||
|
||||
if rawBody == nil {
|
||||
return []byte{}, statusCode, nil
|
||||
}
|
||||
|
||||
rawBodyReplaced := bytes.ReplaceAll(*rawBody, []byte("\"default\": Infinity"), []byte("\"default\": 1e9999"))
|
||||
return rawBodyReplaced, statusCode, nil
|
||||
}
|
||||
|
||||
func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logger log.Logger, req *http.Request, isRaw bool) (*T, *[]byte, int, error) {
|
||||
_, span := tracing.DefaultTracer().Start(ctx, "graphite request")
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
|
@ -209,7 +238,7 @@ func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logge
|
|||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to complete request: %v", err)
|
||||
return nil, nil, http.StatusInternalServerError, fmt.Errorf("failed to complete request: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -218,12 +247,12 @@ func doGraphiteRequest[T any](ctx context.Context, dsInfo *datasourceInfo, logge
|
|||
}
|
||||
}()
|
||||
|
||||
parsedResponse, err := parseResponse[T](res)
|
||||
parsedResponse, rawBody, err := parseResponse[T](res, isRaw, logger)
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to parse response: %v", err)
|
||||
return nil, nil, http.StatusInternalServerError, fmt.Errorf("failed to parse response: %v", err)
|
||||
}
|
||||
|
||||
return parsedResponse, res.StatusCode, nil
|
||||
return parsedResponse, rawBody, res.StatusCode, nil
|
||||
}
|
||||
|
||||
func parseRequestBody[V any](requestBody []byte, logger log.Logger) (*V, error) {
|
||||
|
@ -236,19 +265,28 @@ func parseRequestBody[V any](requestBody []byte, logger log.Logger) (*V, error)
|
|||
return requestJson, nil
|
||||
}
|
||||
|
||||
func parseResponse[V any](res *http.Response) (*V, error) {
|
||||
func parseResponse[V any](res *http.Response, isRaw bool, logger log.Logger) (*V, *[]byte, error) {
|
||||
encoding := res.Header.Get("Content-Encoding")
|
||||
body, err := decode(encoding, res.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to read response: %v", err)
|
||||
}
|
||||
|
||||
if res.StatusCode/100 != 2 {
|
||||
logger.Warn("Request failed", "status", res.Status, "body", string(body))
|
||||
return nil, nil, fmt.Errorf("request failed, status: %d", res.StatusCode)
|
||||
}
|
||||
|
||||
if isRaw {
|
||||
return nil, &body, nil
|
||||
}
|
||||
|
||||
data := new(V)
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return data, nil
|
||||
return data, nil, nil
|
||||
}
|
||||
|
||||
func writeErrorResponse(rw http.ResponseWriter, code int, msg string) {
|
||||
|
|
|
@ -18,12 +18,14 @@ import (
|
|||
)
|
||||
|
||||
type mockRoundTripper struct {
|
||||
respBody []byte
|
||||
status int
|
||||
err error
|
||||
respBody []byte
|
||||
status int
|
||||
err error
|
||||
lastRequest *http.Request
|
||||
}
|
||||
|
||||
func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
m.lastRequest = req
|
||||
if m.err != nil {
|
||||
return nil, m.err
|
||||
}
|
||||
|
@ -129,7 +131,7 @@ func TestHandleEvents(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
svc := &Service{logger: log.NewNullLogger()}
|
||||
|
||||
respBody, status, err := svc.handleEvents(context.Background(), tt.dsInfo, tt.request)
|
||||
respBody, status, err := svc.handleEvents(context.Background(), tt.dsInfo, &tt.request)
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, status)
|
||||
|
||||
|
@ -234,7 +236,7 @@ func TestHandleMetricsFind(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
svc := &Service{logger: log.NewNullLogger()}
|
||||
|
||||
respBody, status, err := svc.handleMetricsFind(context.Background(), tt.dsInfo, tt.request)
|
||||
respBody, status, err := svc.handleMetricsFind(context.Background(), tt.dsInfo, &tt.request)
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, status)
|
||||
|
||||
|
@ -368,7 +370,7 @@ func TestHandleMetricsExpand(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
svc := &Service{logger: log.NewNullLogger()}
|
||||
|
||||
respBody, status, err := svc.handleMetricsExpand(context.Background(), tt.dsInfo, tt.request)
|
||||
respBody, status, err := svc.handleMetricsExpand(context.Background(), tt.dsInfo, &tt.request)
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, status)
|
||||
|
||||
|
@ -392,6 +394,106 @@ func TestHandleMetricsExpand(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHandleFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
responseBody string
|
||||
statusCode int
|
||||
expectError bool
|
||||
errorContains string
|
||||
expectedData string
|
||||
}{
|
||||
{
|
||||
name: "successful functions request",
|
||||
responseBody: `{"sum": {"description": "Sum function"}, "avg": {"description": "Average function"}}`,
|
||||
statusCode: 200,
|
||||
expectError: false,
|
||||
expectedData: `{"sum": {"description": "Sum function"}, "avg": {"description": "Average function"}}`,
|
||||
},
|
||||
{
|
||||
name: "functions with infinity replacement",
|
||||
responseBody: `{"func": {"default": Infinity, "description": "Test function"}}`,
|
||||
statusCode: 200,
|
||||
expectError: false,
|
||||
expectedData: `{"func": {"default": 1e9999, "description": "Test function"}}`,
|
||||
},
|
||||
{
|
||||
name: "empty functions response",
|
||||
responseBody: `{}`,
|
||||
statusCode: 200,
|
||||
expectError: false,
|
||||
expectedData: `{}`,
|
||||
},
|
||||
{
|
||||
name: "functions request server error",
|
||||
responseBody: `{"error": "internal error"}`,
|
||||
statusCode: 500,
|
||||
expectError: true,
|
||||
errorContains: "version request failed",
|
||||
},
|
||||
{
|
||||
name: "functions request not found",
|
||||
responseBody: `{"error": "not found"}`,
|
||||
statusCode: 404,
|
||||
expectError: true,
|
||||
errorContains: "version request failed",
|
||||
},
|
||||
{
|
||||
name: "network error",
|
||||
responseBody: "",
|
||||
statusCode: 0,
|
||||
expectError: true,
|
||||
errorContains: "version request failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var mockTransport *mockRoundTripper
|
||||
|
||||
if tt.name == "network error" {
|
||||
mockTransport = &mockRoundTripper{
|
||||
err: errors.New("network connection failed"),
|
||||
}
|
||||
} else {
|
||||
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.handleFunctions(context.Background(), dsInfo, nil)
|
||||
|
||||
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)
|
||||
assert.Equal(t, tt.expectedData, string(result))
|
||||
}
|
||||
|
||||
// Verify the request was made correctly (except for network error case)
|
||||
if tt.name != "network error" {
|
||||
require.NotNil(t, mockTransport.lastRequest)
|
||||
assert.Equal(t, "http://graphite.example.com/functions", mockTransport.lastRequest.URL.String())
|
||||
assert.Equal(t, http.MethodGet, mockTransport.lastRequest.Method)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleResourceReq_Success(t *testing.T) {
|
||||
mockEvents := []GraphiteEventsResponse{{When: 1234567890, What: "event1"}}
|
||||
mockResp, _ := json.Marshal(mockEvents)
|
||||
|
@ -558,11 +660,10 @@ func TestDoGraphiteRequest(t *testing.T) {
|
|||
URL: "http://graphite.grafana",
|
||||
HTTPClient: &http.Client{Transport: &mockRoundTripper{respBody: []byte("[]"), status: 500}},
|
||||
},
|
||||
method: "GET",
|
||||
headers: map[string]string{},
|
||||
expectedStatus: 500,
|
||||
expectError: false,
|
||||
expectedData: []GraphiteEventsResponse{},
|
||||
method: "GET",
|
||||
headers: map[string]string{},
|
||||
expectError: true,
|
||||
errorContains: "request failed, status: 500",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -594,7 +695,7 @@ func TestDoGraphiteRequest(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
result, status, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, tt.dsInfo, svc.logger, req)
|
||||
result, _, status, err := doGraphiteRequest[[]GraphiteEventsResponse](ctx, tt.dsInfo, svc.logger, req, false)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
|
@ -653,7 +754,7 @@ func TestDoGraphiteRequestGenericTypes(t *testing.T) {
|
|||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, status, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, svc.logger, req)
|
||||
result, _, status, err := doGraphiteRequest[[]GraphiteMetricsFindResponse](ctx, dsInfo, svc.logger, req, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
|
@ -681,7 +782,7 @@ func TestDoGraphiteRequestGenericTypes(t *testing.T) {
|
|||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, status, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, svc.logger, req)
|
||||
result, _, status, err := doGraphiteRequest[GraphiteMetricsExpandResponse](ctx, dsInfo, svc.logger, req, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
|
@ -825,7 +926,7 @@ func TestParseResponse(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := parseResponse[[]GraphiteEventsResponse](tt.response)
|
||||
result, _, err := parseResponse[[]GraphiteEventsResponse](tt.response, false, log.NewNullLogger())
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
|
|
|
@ -947,7 +947,7 @@ export class GraphiteDatasource
|
|||
return this.getFuncDefs();
|
||||
}
|
||||
|
||||
getFuncDefs() {
|
||||
async getFuncDefs() {
|
||||
if (this.funcDefsPromise !== null) {
|
||||
return this.funcDefsPromise;
|
||||
}
|
||||
|
@ -966,6 +966,12 @@ export class GraphiteDatasource
|
|||
responseType: 'text' as const,
|
||||
};
|
||||
|
||||
if (config.featureToggles.graphiteBackendMode) {
|
||||
const functions = await this.getResource<string>('functions');
|
||||
this.funcDefs = gfunc.parseFuncDefs(functions);
|
||||
return this.funcDefs;
|
||||
}
|
||||
|
||||
return lastValueFrom(
|
||||
this.doGraphiteRequest(httpOptions).pipe(
|
||||
map((results: FetchResponse) => {
|
||||
|
|
Loading…
Reference in New Issue