Tempo: Move health check requests to backend (#111668)
Actionlint / Lint GitHub Actions files (push) Waiting to run Details
Backend Code Checks / Detect whether code changed (push) Waiting to run Details
Backend Code Checks / Validate Backend Configs (push) Blocked by required conditions Details
Backend Unit Tests / Detect whether code changed (push) Waiting to run Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions Details
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions Details
CodeQL checks / Detect whether code changed (push) Waiting to run Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions Details
Lint Frontend / Detect whether code changed (push) Waiting to run Details
Lint Frontend / Lint (push) Blocked by required conditions Details
Lint Frontend / Typecheck (push) Blocked by required conditions Details
Lint Frontend / Verify API clients (push) Waiting to run Details
Lint Frontend / Verify API clients (enterprise) (push) Waiting to run Details
golangci-lint / Detect whether code changed (push) Waiting to run Details
golangci-lint / go-fmt (push) Blocked by required conditions Details
golangci-lint / lint-go (push) Blocked by required conditions Details
Verify i18n / verify-i18n (push) Waiting to run Details
End-to-end tests / Detect whether code changed (push) Waiting to run Details
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions Details
End-to-end tests / Build E2E test runner (push) Blocked by required conditions Details
End-to-end tests / push-docker-image (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Blocked by required conditions Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Blocked by required conditions Details
End-to-end tests / run-azure-monitor-e2e (push) Blocked by required conditions Details
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions Details
End-to-end tests / A11y test (push) Blocked by required conditions Details
End-to-end tests / Publish metrics (push) Blocked by required conditions Details
End-to-end tests / All E2E tests complete (push) Blocked by required conditions Details
Frontend tests / Detect whether code changed (push) Waiting to run Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (1, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (10, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (11, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (12, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (13, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (14, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (15, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (16, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (2, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (3, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (4, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (5, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (6, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (7, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (8, 16) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (9, 16) (push) Blocked by required conditions Details
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions Details
Frontend tests / Packages unit tests (push) Blocked by required conditions Details
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions Details
Integration Tests / Detect whether code changed (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (1/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (2/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (3/4) (push) Blocked by required conditions Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (4/4) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (10/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (11/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (12/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (13/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (14/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (15/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (16/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/16) (push) Blocked by required conditions Details
Integration Tests / MySQL (${{ matrix.shard }}) (9/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (10/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (11/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (12/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (13/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (14/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (15/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (16/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/16) (push) Blocked by required conditions Details
Integration Tests / Postgres (${{ matrix.shard }}) (9/16) (push) Blocked by required conditions Details
Integration Tests / All backend integration tests complete (push) Blocked by required conditions Details
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run Details
Shellcheck / Shellcheck scripts (push) Waiting to run Details
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run Details
Run Storybook a11y tests / Run Storybook a11y tests (push) Blocked by required conditions Details
Swagger generated code / Detect whether code changed (push) Waiting to run Details
Swagger generated code / Verify committed API specs match (push) Blocked by required conditions Details
Dispatch sync to mirror / dispatch-job (push) Waiting to run Details
Crowdin Upload Action / upload-sources-to-crowdin (push) Has been cancelled Details

* move tempo health check requests to backend

* fix ci and tests

* add checkhealth test to backend
This commit is contained in:
Gareth 2025-09-30 17:57:45 +08:00 committed by GitHub
parent 3668d02650
commit 1ebfc3fea2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 182 additions and 106 deletions

View File

@ -13,6 +13,7 @@ import (
) )
var ( var (
_ backend.CheckHealthHandler = (*Datasource)(nil)
_ backend.QueryDataHandler = (*Datasource)(nil) _ backend.QueryDataHandler = (*Datasource)(nil)
_ backend.StreamHandler = (*Datasource)(nil) _ backend.StreamHandler = (*Datasource)(nil)
_ backend.CallResourceHandler = (*Datasource)(nil) _ backend.CallResourceHandler = (*Datasource)(nil)
@ -28,6 +29,10 @@ func NewDatasource(c context.Context, b backend.DataSourceInstanceSettings) (ins
}, nil }, nil
} }
func (d *Datasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
return d.Service.CheckHealth(ctx, req)
}
func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
return d.Service.QueryData(ctx, req) return d.Service.QueryData(ctx, req)
} }

View File

@ -2,6 +2,8 @@ package tempo
import ( import (
"context" "context"
"encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -156,6 +158,112 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
return s.resourceHandler.CallResource(ctx, req, sender) return s.resourceHandler.CallResource(ctx, req, sender)
} }
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
var streamingEnabled bool
var jsonData map[string]interface{}
pluginCtx := backend.PluginConfigFromContext(ctx)
dsInfo, err := s.getDSInfo(ctx, pluginCtx)
if err != nil {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: err.Error(),
}, nil
}
if pluginCtx.DataSourceInstanceSettings != nil && pluginCtx.DataSourceInstanceSettings.JSONData != nil {
if err := json.Unmarshal(pluginCtx.DataSourceInstanceSettings.JSONData, &jsonData); err == nil {
if streaming, ok := jsonData["streamingEnabled"].(map[string]interface{}); ok {
if searchEnabled, ok := streaming["search"].(bool); ok && searchEnabled {
streamingEnabled = true
}
}
}
}
if streamingEnabled {
if dsInfo.StreamingClient == nil {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: "Streaming client is not available",
}, nil
}
currentTime := time.Now()
queryStartTime := currentTime.Add(-15 * time.Minute)
searchRequest := &tempopb.SearchRequest{
Query: "{}",
Start: uint32(queryStartTime.Unix()),
End: uint32(currentTime.Unix()),
Limit: 1,
}
streamingConnection, err := dsInfo.StreamingClient.Search(ctx, searchRequest)
if err != nil {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: err.Error(),
}, nil
}
_, err = streamingConnection.Recv()
if err != nil && !errors.Is(err, io.EOF) {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: err.Error(),
}, nil
}
return &backend.CheckHealthResult{
Status: backend.HealthStatusOk,
Message: "Data source is working. Streaming test succeeded.",
}, nil
}
parsedURL, err := url.Parse(dsInfo.URL)
if err != nil {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: err.Error(),
}, nil
}
parsedURL.Path = path.Join(parsedURL.Path, "api/echo")
httpReq, err := http.NewRequestWithContext(ctx, "GET", parsedURL.String(), nil)
if err != nil {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: err.Error(),
}, nil
}
resp, err := dsInfo.HTTPClient.Do(httpReq)
if err != nil {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: err.Error(),
}, nil
}
defer func() {
if err := resp.Body.Close(); err != nil {
s.logger.Warn("Failed to close response body", "error", err)
}
}()
if resp.StatusCode != 200 {
return &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: fmt.Sprintf("Tempo echo endpoint returned status %d", resp.StatusCode),
}, nil
}
return &backend.CheckHealthResult{
Status: backend.HealthStatusOk,
Message: "Data source is working",
}, nil
}
// handleTags handles requests to /tags resource // handleTags handles requests to /tags resource
func (s *Service) handleTags(rw http.ResponseWriter, req *http.Request) { func (s *Service) handleTags(rw http.ResponseWriter, req *http.Request) {
s.proxyToTempo(rw, req, "api/v2/search/tags") s.proxyToTempo(rw, req, "api/v2/search/tags")

View File

@ -0,0 +1,67 @@
package tempo
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/stretchr/testify/assert"
)
func TestCheckHealth(t *testing.T) {
tests := []struct {
name string
httpStatusCode int
expectedStatus backend.HealthStatus
expectedMessage string
}{
{
name: "successful health check",
httpStatusCode: 200,
expectedStatus: backend.HealthStatusOk,
expectedMessage: "Data source is working",
},
{
name: "http error",
httpStatusCode: 500,
expectedStatus: backend.HealthStatusError,
expectedMessage: "Tempo echo endpoint returned status 500",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tt.httpStatusCode)
}))
defer server.Close()
pluginCtx := backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{
URL: server.URL,
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
dsInfo := &DatasourceInfo{
URL: server.URL,
HTTPClient: server.Client(),
StreamingClient: nil,
}
return dsInfo, nil
})
service := &Service{im: im}
ctx := backend.WithPluginContext(context.Background(), pluginCtx)
result, err := service.CheckHealth(ctx, &backend.CheckHealthRequest{})
assert.NoError(t, err)
assert.Equal(t, tt.expectedStatus, result.Status)
assert.Contains(t, result.Message, tt.expectedMessage)
})
}
}

View File

@ -52,17 +52,6 @@ import { createTempoDatasource } from './test/mocks';
import { initTemplateSrv } from './test/test_utils'; import { initTemplateSrv } from './test/test_utils';
import { TempoJsonData, TempoQuery } from './types'; import { TempoJsonData, TempoQuery } from './types';
let mockObservable: () => Observable<unknown>;
jest.mock('@grafana/runtime', () => {
return {
...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => ({
fetch: mockObservable,
_request: mockObservable,
}),
};
});
describe('Tempo data source', () => { describe('Tempo data source', () => {
// Mock the console error so that running the test suite doesnt throw the error // Mock the console error so that running the test suite doesnt throw the error
const origError = console.error; const origError = console.error;
@ -339,20 +328,6 @@ describe('Tempo data source', () => {
expect(edgesFrame.meta?.preferredVisualisationType).toBe('nodeGraph'); expect(edgesFrame.meta?.preferredVisualisationType).toBe('nodeGraph');
}); });
describe('test the testDatasource function', () => {
it('should return a success msg if response.ok is true', async () => {
mockObservable = () => of({ ok: true });
const handleStreamingQuery = jest
.spyOn(TempoDatasource.prototype, 'handleStreamingQuery')
.mockImplementation(() => of({ data: [] }));
const ds = new TempoDatasource(defaultSettings);
const response = await ds.testDatasource();
expect(response.status).toBe('success');
expect(handleStreamingQuery).toHaveBeenCalled();
});
});
describe('test the metadataRequest function', () => { describe('test the metadataRequest function', () => {
it('should return the data from getResource', async () => { it('should return the data from getResource', async () => {
const ds = new TempoDatasource(defaultSettings); const ds = new TempoDatasource(defaultSettings);

View File

@ -1,5 +1,5 @@
import { groupBy } from 'lodash'; import { groupBy } from 'lodash';
import { EMPTY, forkJoin, from, lastValueFrom, merge, Observable, of } from 'rxjs'; import { EMPTY, from, merge, Observable, of } from 'rxjs';
import { catchError, concatMap, finalize, map, mergeMap, toArray } from 'rxjs/operators'; import { catchError, concatMap, finalize, map, mergeMap, toArray } from 'rxjs/operators';
import { import {
@ -931,86 +931,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
} }
async testDatasource(): Promise<TestDataSourceResponse> { async testDatasource(): Promise<TestDataSourceResponse> {
const observables = []; return await super.testDatasource();
const options: BackendSrvRequest = {
headers: {},
method: 'GET',
url: `${this.instanceSettings.url}/api/echo`,
};
observables.push(
getBackendSrv()
.fetch(options)
.pipe(
mergeMap(() => {
return of({ status: 'success', message: 'Health check succeeded' });
}),
catchError((err) => {
return of({
status: 'error',
message: getErrorMessage(err?.data?.message, 'Unable to connect with Tempo'),
});
})
)
);
if (this.streamingEnabled?.search) {
const now = new Date();
const from = new Date(now);
from.setMinutes(from.getMinutes() - 15);
observables.push(
this.handleStreamingQuery(
{
range: {
from: dateTime(from),
to: dateTime(now),
raw: { from: 'now-15m', to: 'now' },
},
requestId: '',
interval: '',
intervalMs: 0,
scopedVars: {},
targets: [],
timezone: '',
app: '',
startTime: 0,
},
[
{
datasource: this.instanceSettings,
limit: 1,
query: '{}',
queryType: 'traceql',
refId: 'A',
tableType: SearchTableType.Traces,
filters: [],
},
],
'{}'
).pipe(
mergeMap(() => {
return of({ status: 'success', message: 'Streaming test succeeded.' });
}),
catchError((err) => {
return of({
status: 'error',
message: getErrorMessage(err?.data?.message, 'Test for streaming failed, consider disabling streaming'),
});
})
)
);
}
return await lastValueFrom(
forkJoin(observables).pipe(
mergeMap((observableResults) => {
const erroredResult = observableResults.find((result) => result.status !== 'success');
return erroredResult
? of(erroredResult)
: of({ status: 'success', message: 'Successfully connected to Tempo data source.' });
})
)
);
} }
getQueryDisplayText(query: TempoQuery) { getQueryDisplayText(query: TempoQuery) {