Graphite: Backend version endpoint (#110774)
* 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 * Support the version endpoint * 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
This commit is contained in:
@@ -27,7 +27,7 @@ func (s *Service) newResourceMux() *http.ServeMux {
|
||||
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))
|
||||
|
||||
mux.HandleFunc("/version", handleResourceReq(s.handleVersion, s))
|
||||
return mux
|
||||
}
|
||||
|
||||
@@ -99,7 +99,6 @@ func (s *Service) handleEvents(ctx context.Context, dsInfo *datasourceInfo, even
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "events/get_data",
|
||||
Method: http.MethodGet,
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -179,7 +178,6 @@ func (s *Service) handleMetricsExpand(ctx context.Context, dsInfo *datasourceInf
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "metrics/expand",
|
||||
Method: http.MethodGet,
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -215,7 +213,6 @@ func (s *Service) handleTagsAutocomplete(ctx context.Context, dsInfo *datasource
|
||||
}
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "tags/autoComplete/tags",
|
||||
Method: http.MethodGet,
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -247,7 +244,6 @@ func (s *Service) handleTagValuesAutocomplete(ctx context.Context, dsInfo *datas
|
||||
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "tags/autoComplete/values",
|
||||
Method: http.MethodGet,
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -267,10 +263,30 @@ func (s *Service) handleTagValuesAutocomplete(ctx context.Context, dsInfo *datas
|
||||
return tagValuesResponse, statusCode, nil
|
||||
}
|
||||
|
||||
func (s *Service) handleVersion(ctx context.Context, dsInfo *datasourceInfo, _ *any) ([]byte, int, error) {
|
||||
req, err := s.createRequest(ctx, dsInfo, URLParams{
|
||||
SubPath: "version",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create version request %v", err)
|
||||
}
|
||||
|
||||
version, _, statusCode, err := doGraphiteRequest[string](ctx, dsInfo, s.logger, req, false)
|
||||
if err != nil {
|
||||
return nil, statusCode, fmt.Errorf("version request failed: %v", err)
|
||||
}
|
||||
|
||||
versionResponse, err := json.Marshal(version)
|
||||
if err != nil {
|
||||
return nil, http.StatusInternalServerError, fmt.Errorf("failed to marshal version response: %s", err)
|
||||
}
|
||||
|
||||
return versionResponse, statusCode, nil
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -620,6 +620,86 @@ func TestHandleTagValuesAutocomplete(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
responseBody string
|
||||
statusCode int
|
||||
expectError bool
|
||||
errorContains string
|
||||
expectedData string
|
||||
}{
|
||||
{
|
||||
name: "successful version request",
|
||||
responseBody: `"1.1.10"`,
|
||||
statusCode: 200,
|
||||
expectedData: "1.1.10",
|
||||
},
|
||||
{
|
||||
name: "version with build info",
|
||||
responseBody: `"1.1.10-pre1"`,
|
||||
statusCode: 200,
|
||||
expectedData: "1.1.10-pre1",
|
||||
},
|
||||
{
|
||||
name: "version request server error - invalid JSON causes parse error",
|
||||
responseBody: `{"error": "internal error"}`,
|
||||
statusCode: 500,
|
||||
expectError: true,
|
||||
errorContains: "version request failed",
|
||||
},
|
||||
{
|
||||
name: "version request not found - invalid JSON causes parse error",
|
||||
responseBody: `{"error": "not found"}`,
|
||||
statusCode: 404,
|
||||
expectError: true,
|
||||
errorContains: "version 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.handleVersion(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)
|
||||
|
||||
var version string
|
||||
err = json.Unmarshal(result, &version)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedData, version)
|
||||
}
|
||||
|
||||
if !tt.expectError {
|
||||
expectedURL := "http://graphite.example.com/version"
|
||||
assert.Equal(t, expectedURL, mockTransport.lastRequest.URL.String())
|
||||
assert.Equal(t, http.MethodGet, mockTransport.lastRequest.Method)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -871,7 +871,7 @@ export class GraphiteDatasource
|
||||
return lastValueFrom(this.doGraphiteRequest(httpOptions).pipe(mapToTags()));
|
||||
}
|
||||
|
||||
getVersion(optionalOptions: any) {
|
||||
async getVersion(optionalOptions: any) {
|
||||
const options = optionalOptions || {};
|
||||
|
||||
const httpOptions = {
|
||||
@@ -880,6 +880,12 @@ export class GraphiteDatasource
|
||||
requestId: options.requestId,
|
||||
};
|
||||
|
||||
if (config.featureToggles.graphiteBackendMode) {
|
||||
const version = await this.getResource<string>('version');
|
||||
const semver = new SemVer(version);
|
||||
return valid(semver) ? version : '';
|
||||
}
|
||||
|
||||
return lastValueFrom(
|
||||
this.doGraphiteRequest(httpOptions).pipe(
|
||||
map((results: FetchResponse) => {
|
||||
|
||||
Reference in New Issue
Block a user