Zipkin: Move query running to backend (#96404)
* Rename method applyTemplateVariables so it used by backend * Zipkin: Move query running to backend * Fix error source * Hndle invalid query and return error response
This commit is contained in:
@@ -127,6 +127,10 @@ func (z *ZipkinClient) Trace(traceId string) ([]model.SpanModel, error) {
|
||||
}
|
||||
|
||||
res, err := z.httpClient.Get(traceUrl)
|
||||
if err != nil {
|
||||
return trace, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if res != nil {
|
||||
if err = res.Body.Close(); err != nil {
|
||||
@@ -134,9 +138,6 @@ func (z *ZipkinClient) Trace(traceId string) ([]model.SpanModel, error) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return trace, err
|
||||
}
|
||||
if err := json.NewDecoder(res.Body).Decode(&trace); err != nil {
|
||||
return trace, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
@@ -250,7 +251,8 @@ func TestZipkinClient_Trace(t *testing.T) {
|
||||
Tags: map[string]string{"key1": "value1"},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
expectError: false,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Invalid traceID",
|
||||
@@ -261,13 +263,31 @@ func TestZipkinClient_Trace(t *testing.T) {
|
||||
expectError: true,
|
||||
expectedError: "invalid/empty traceId",
|
||||
},
|
||||
{
|
||||
name: "Special characters traceID",
|
||||
traceID: "a/b",
|
||||
mockResponse: `[{"traceId":"00000000000004d2","id":"0000000000000001","name":"operation1","tags":{"key1":"value1"}}]`,
|
||||
mockStatusCode: http.StatusOK,
|
||||
expectedResult: []model.SpanModel{
|
||||
{
|
||||
SpanContext: model.SpanContext{
|
||||
TraceID: model.TraceID{Low: 1234},
|
||||
ID: model.ID(1),
|
||||
},
|
||||
Name: "operation1",
|
||||
Tags: map[string]string{"key1": "value1"},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
expectedError: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var client ZipkinClient
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/api/v2/trace/"+tt.traceID, r.URL.Path)
|
||||
assert.Contains(t, r.URL.String(), "/api/v2/trace/"+url.QueryEscape(tt.traceID))
|
||||
w.WriteHeader(tt.mockStatusCode)
|
||||
_, _ = w.Write([]byte(tt.mockResponse))
|
||||
}))
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
func queryData(ctx context.Context, dsInfo *datasourceInfo, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
response := backend.NewQueryDataResponse()
|
||||
logger := dsInfo.ZipkinClient.logger.FromContext(ctx)
|
||||
|
||||
for _, q := range req.Queries {
|
||||
query, err := loadQuery(q)
|
||||
if err != nil {
|
||||
es := backend.ErrorSourcePlugin
|
||||
if backend.IsDownstreamError(err) {
|
||||
es = backend.ErrorSourceDownstream
|
||||
}
|
||||
response.Responses[q.RefID] = backend.DataResponse{
|
||||
Error: err,
|
||||
ErrorSource: es,
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch query.QueryType {
|
||||
case zipkinQueryTypeUpload:
|
||||
logger.Debug("upload query type is not supported in backend mode")
|
||||
response.Responses[q.RefID] = backend.DataResponse{
|
||||
Error: fmt.Errorf("unsupported query type %s. only available in frontend mode", query.QueryType),
|
||||
ErrorSource: backend.ErrorSourcePlugin,
|
||||
}
|
||||
default:
|
||||
traces, err := dsInfo.ZipkinClient.Trace(query.Query)
|
||||
if err != nil {
|
||||
es := backend.ErrorSourcePlugin
|
||||
if backend.IsDownstreamHTTPError(err) {
|
||||
es = backend.ErrorSourceDownstream
|
||||
}
|
||||
response.Responses[q.RefID] = backend.DataResponse{
|
||||
Error: err,
|
||||
ErrorSource: es,
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
frame := transformResponse(traces, q.RefID)
|
||||
response.Responses[q.RefID] = backend.DataResponse{
|
||||
Frames: []*data.Frame{frame},
|
||||
}
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
type zipkinQueryType string
|
||||
|
||||
const (
|
||||
zipkinQueryTypeTraceId zipkinQueryType = "traceID"
|
||||
zipkinQueryTypeUpload zipkinQueryType = "upload"
|
||||
)
|
||||
|
||||
type zipkinQuery struct {
|
||||
Query string `json:"query,omitempty"`
|
||||
QueryType zipkinQueryType `json:"queryType,omitempty"`
|
||||
}
|
||||
|
||||
func loadQuery(backendQuery backend.DataQuery) (zipkinQuery, error) {
|
||||
var query zipkinQuery
|
||||
err := json.Unmarshal(backendQuery.JSON, &query)
|
||||
if err != nil {
|
||||
return query, backend.DownstreamError(fmt.Errorf("error while parsing the query json. %w", err))
|
||||
}
|
||||
return query, err
|
||||
}
|
||||
|
||||
type TraceKeyValuePair struct {
|
||||
Key string `json:"key"`
|
||||
Value interface{} `json:"value"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type TraceLog struct {
|
||||
Timestamp int64
|
||||
Fields []TraceKeyValuePair
|
||||
}
|
||||
|
||||
func transformResponse(zipkinSpans []model.SpanModel, refId string) *data.Frame {
|
||||
newFrame := data.NewFrame(refId,
|
||||
data.NewField("traceID", nil, []string{}),
|
||||
data.NewField("spanID", nil, []string{}),
|
||||
data.NewField("parentSpanID", nil, []*string{}),
|
||||
data.NewField("operationName", nil, []string{}),
|
||||
data.NewField("serviceName", nil, []string{}),
|
||||
data.NewField("serviceTags", nil, []json.RawMessage{}),
|
||||
data.NewField("startTime", nil, []float64{}),
|
||||
data.NewField("duration", nil, []float64{}),
|
||||
data.NewField("logs", nil, []json.RawMessage{}),
|
||||
data.NewField("tags", nil, []json.RawMessage{}),
|
||||
)
|
||||
|
||||
newFrame.Meta = &data.FrameMeta{
|
||||
PreferredVisualization: "trace",
|
||||
Custom: map[string]interface{}{
|
||||
"traceFormat": "zipkin",
|
||||
},
|
||||
}
|
||||
|
||||
// go through each span and add to the frame
|
||||
for _, span := range zipkinSpans {
|
||||
var parentSpanIdString *string
|
||||
if span.ParentID != nil {
|
||||
s := span.ParentID.String()
|
||||
parentSpanIdString = &s
|
||||
}
|
||||
var serviceTags json.RawMessage
|
||||
serviceTagsMarshaled, err := json.Marshal(getServiceTags(span))
|
||||
if err == nil {
|
||||
serviceTags = json.RawMessage(serviceTagsMarshaled)
|
||||
}
|
||||
|
||||
var logs json.RawMessage
|
||||
logsMarshaled, err := json.Marshal(transformAnnotationsToTraceLogs(span.Annotations))
|
||||
if err == nil {
|
||||
logs = json.RawMessage(logsMarshaled)
|
||||
}
|
||||
|
||||
var tags json.RawMessage
|
||||
tagsMarshaled, err := json.Marshal(transformTags(span))
|
||||
if err == nil {
|
||||
tags = json.RawMessage(tagsMarshaled)
|
||||
}
|
||||
newFrame.AppendRow(
|
||||
span.TraceID.String(),
|
||||
span.ID.String(),
|
||||
parentSpanIdString,
|
||||
span.Name,
|
||||
getServiceName(span),
|
||||
serviceTags,
|
||||
float64(span.Timestamp.UnixMicro())/1000,
|
||||
float64(span.Duration.Microseconds())/1000,
|
||||
logs,
|
||||
tags,
|
||||
)
|
||||
}
|
||||
return newFrame
|
||||
}
|
||||
|
||||
func getServiceName(span model.SpanModel) string {
|
||||
if span.LocalEndpoint != nil && span.LocalEndpoint.ServiceName != "" {
|
||||
return span.LocalEndpoint.ServiceName
|
||||
} else if span.RemoteEndpoint != nil && span.RemoteEndpoint.ServiceName != "" {
|
||||
return span.RemoteEndpoint.ServiceName
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func getServiceTags(span model.SpanModel) []TraceKeyValuePair {
|
||||
tags := make([]TraceKeyValuePair, 0, 4)
|
||||
endpoint := span.LocalEndpoint
|
||||
endpointType := "local"
|
||||
|
||||
if endpoint == nil {
|
||||
endpoint = span.RemoteEndpoint
|
||||
endpointType = "remote"
|
||||
}
|
||||
|
||||
if endpoint == nil {
|
||||
return tags
|
||||
}
|
||||
|
||||
if endpoint.IPv4 != nil {
|
||||
tag := valueToTag("ipv4", endpoint.IPv4.String())
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
if endpoint.IPv6 != nil {
|
||||
tag := valueToTag("ipv6", endpoint.IPv6.String())
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
if endpoint.Port != 0 {
|
||||
tag := valueToTag("port", endpoint.Port)
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
if endpointType != "" {
|
||||
tag := valueToTag("endpointType", endpointType)
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func valueToTag(key string, value interface{}) TraceKeyValuePair {
|
||||
return TraceKeyValuePair{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func transformAnnotationsToTraceLogs(annotations []model.Annotation) []TraceLog {
|
||||
transformed := make([]TraceLog, 0, len(annotations))
|
||||
if len(annotations) == 0 {
|
||||
return transformed
|
||||
}
|
||||
|
||||
for _, annotation := range annotations {
|
||||
transformedAnnotation := TraceLog{
|
||||
Timestamp: annotation.Timestamp.UnixMicro(),
|
||||
Fields: []TraceKeyValuePair{
|
||||
{
|
||||
Key: "annotation",
|
||||
Value: annotation.Value,
|
||||
},
|
||||
},
|
||||
}
|
||||
transformed = append(transformed, transformedAnnotation)
|
||||
}
|
||||
return transformed
|
||||
}
|
||||
|
||||
func transformTags(span model.SpanModel) []TraceKeyValuePair {
|
||||
tags := make([]TraceKeyValuePair, 0, len(span.Tags)+2)
|
||||
|
||||
for key, value := range span.Tags {
|
||||
if key == "error" {
|
||||
// Remap error tag to show error icon and include error details
|
||||
tags = append(tags, TraceKeyValuePair{
|
||||
Key: "error",
|
||||
Value: true,
|
||||
})
|
||||
tags = append(tags, TraceKeyValuePair{
|
||||
Key: "errorValue",
|
||||
Value: value,
|
||||
})
|
||||
} else {
|
||||
tags = append(tags, TraceKeyValuePair{
|
||||
Key: key,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend kind if present
|
||||
if span.Kind != "" {
|
||||
tags = append([]TraceKeyValuePair{{Key: "kind", Value: span.Kind}}, tags...)
|
||||
}
|
||||
|
||||
// Prepend shared if present
|
||||
if span.Shared {
|
||||
tags = append([]TraceKeyValuePair{{Key: "shared", Value: span.Shared}}, tags...)
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/experimental"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
func TestTransformResponse(t *testing.T) {
|
||||
t.Run("simple_trace", func(t *testing.T) {
|
||||
span1 := model.SpanModel{
|
||||
SpanContext: model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 123,
|
||||
Low: 456,
|
||||
},
|
||||
ID: 1,
|
||||
},
|
||||
Name: "span 1",
|
||||
Kind: "CLIENT",
|
||||
Timestamp: time.Unix(0, 1*int64(time.Microsecond)),
|
||||
Duration: 10 * time.Microsecond,
|
||||
LocalEndpoint: &model.Endpoint{
|
||||
ServiceName: "service 1",
|
||||
IPv4: net.IPv4(1, 0, 0, 1),
|
||||
Port: 42,
|
||||
},
|
||||
Annotations: []model.Annotation{
|
||||
{Timestamp: time.Unix(0, 2*int64(time.Microsecond)), Value: "annotation text"},
|
||||
{Timestamp: time.Unix(0, 6*int64(time.Microsecond)), Value: "annotation text 3"},
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"tag1": "val1",
|
||||
"tag2": "val2",
|
||||
},
|
||||
}
|
||||
span2 := model.SpanModel{
|
||||
SpanContext: model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 123,
|
||||
Low: 456,
|
||||
},
|
||||
ID: 2,
|
||||
ParentID: &span1.ID,
|
||||
},
|
||||
Name: "span 2",
|
||||
Timestamp: time.Unix(0, 4*int64(time.Microsecond)),
|
||||
Duration: 5 * time.Microsecond,
|
||||
LocalEndpoint: &model.Endpoint{
|
||||
ServiceName: "service 2",
|
||||
IPv4: net.IPv4(1, 0, 0, 1),
|
||||
},
|
||||
Tags: map[string]string{
|
||||
"error": "404",
|
||||
},
|
||||
}
|
||||
|
||||
span3 := model.SpanModel{
|
||||
SpanContext: model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 123,
|
||||
Low: 456,
|
||||
},
|
||||
ID: 3,
|
||||
ParentID: &span1.ID,
|
||||
},
|
||||
Name: "span 3",
|
||||
Timestamp: time.Unix(0, 6*int64(time.Microsecond)),
|
||||
Duration: 7 * time.Microsecond,
|
||||
RemoteEndpoint: &model.Endpoint{
|
||||
ServiceName: "spanstore-jdbc",
|
||||
IPv6: net.ParseIP("::1"),
|
||||
},
|
||||
}
|
||||
|
||||
spans := []model.SpanModel{span1, span2, span3}
|
||||
frames := transformResponse(spans, "test")
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "simple_trace.golden", frames, false)
|
||||
})
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "custom": {
|
||||
// "traceFormat": "zipkin"
|
||||
// },
|
||||
// "preferredVisualisationType": "trace"
|
||||
// }
|
||||
// Name: test
|
||||
// Dimensions: 10 Fields by 3 Rows
|
||||
// +----------------------------------+------------------+--------------------+---------------------+-------------------+-----------------------------------------------------------------------------------------------------+-----------------+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------+
|
||||
// | Name: traceID | Name: spanID | Name: parentSpanID | Name: operationName | Name: serviceName | Name: serviceTags | Name: startTime | Name: duration | Name: logs | Name: tags |
|
||||
// | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string | Type: []*string | Type: []string | Type: []string | Type: []json.RawMessage | Type: []float64 | Type: []float64 | Type: []json.RawMessage | Type: []json.RawMessage |
|
||||
// +----------------------------------+------------------+--------------------+---------------------+-------------------+-----------------------------------------------------------------------------------------------------+-----------------+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------+
|
||||
// | 000000000000007b00000000000001c8 | 0000000000000001 | null | span 1 | service 1 | [{"key":"ipv4","value":"1.0.0.1"},{"key":"port","value":42},{"key":"endpointType","value":"local"}] | 0.001 | 0.01 | [{"Timestamp":2,"Fields":[{"key":"annotation","value":"annotation text"}],"Name":""},{"Timestamp":6,"Fields":[{"key":"annotation","value":"annotation text 3"}],"Name":""}] | [{"key":"kind","value":"CLIENT"},{"key":"tag2","value":"val2"},{"key":"tag1","value":"val1"}] |
|
||||
// | 000000000000007b00000000000001c8 | 0000000000000002 | 0000000000000001 | span 2 | service 2 | [{"key":"ipv4","value":"1.0.0.1"},{"key":"endpointType","value":"local"}] | 0.004 | 0.005 | [] | [{"key":"error","value":true},{"key":"errorValue","value":"404"}] |
|
||||
// | 000000000000007b00000000000001c8 | 0000000000000003 | 0000000000000001 | span 3 | spanstore-jdbc | [{"key":"ipv6","value":"::1"},{"key":"endpointType","value":"remote"}] | 0.006 | 0.007 | [] | [] |
|
||||
// +----------------------------------+------------------+--------------------+---------------------+-------------------+-----------------------------------------------------------------------------------------------------+-----------------+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"custom": {
|
||||
"traceFormat": "zipkin"
|
||||
},
|
||||
"preferredVisualisationType": "trace"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "traceID",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spanID",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "parentSpanID",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "operationName",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "serviceName",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "serviceTags",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTime",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "float64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "float64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "logs",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"type": "other",
|
||||
"typeInfo": {
|
||||
"frame": "json.RawMessage"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"000000000000007b00000000000001c8",
|
||||
"000000000000007b00000000000001c8",
|
||||
"000000000000007b00000000000001c8"
|
||||
],
|
||||
[
|
||||
"0000000000000001",
|
||||
"0000000000000002",
|
||||
"0000000000000003"
|
||||
],
|
||||
[
|
||||
null,
|
||||
"0000000000000001",
|
||||
"0000000000000001"
|
||||
],
|
||||
[
|
||||
"span 1",
|
||||
"span 2",
|
||||
"span 3"
|
||||
],
|
||||
[
|
||||
"service 1",
|
||||
"service 2",
|
||||
"spanstore-jdbc"
|
||||
],
|
||||
[
|
||||
[
|
||||
{
|
||||
"key": "ipv4",
|
||||
"value": "1.0.0.1"
|
||||
},
|
||||
{
|
||||
"key": "port",
|
||||
"value": 42
|
||||
},
|
||||
{
|
||||
"key": "endpointType",
|
||||
"value": "local"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"key": "ipv4",
|
||||
"value": "1.0.0.1"
|
||||
},
|
||||
{
|
||||
"key": "endpointType",
|
||||
"value": "local"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"key": "ipv6",
|
||||
"value": "::1"
|
||||
},
|
||||
{
|
||||
"key": "endpointType",
|
||||
"value": "remote"
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
0.001,
|
||||
0.004,
|
||||
0.006
|
||||
],
|
||||
[
|
||||
0.01,
|
||||
0.005,
|
||||
0.007
|
||||
],
|
||||
[
|
||||
[
|
||||
{
|
||||
"Timestamp": 2,
|
||||
"Fields": [
|
||||
{
|
||||
"key": "annotation",
|
||||
"value": "annotation text"
|
||||
}
|
||||
],
|
||||
"Name": ""
|
||||
},
|
||||
{
|
||||
"Timestamp": 6,
|
||||
"Fields": [
|
||||
{
|
||||
"key": "annotation",
|
||||
"value": "annotation text 3"
|
||||
}
|
||||
],
|
||||
"Name": ""
|
||||
}
|
||||
],
|
||||
[],
|
||||
[]
|
||||
],
|
||||
[
|
||||
[
|
||||
{
|
||||
"key": "kind",
|
||||
"value": "CLIENT"
|
||||
},
|
||||
{
|
||||
"key": "tag2",
|
||||
"value": "val2"
|
||||
},
|
||||
{
|
||||
"key": "tag1",
|
||||
"value": "val1"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"key": "error",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"key": "errorValue",
|
||||
"value": "404"
|
||||
}
|
||||
],
|
||||
[]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func (s *Service) getDSInfo(ctx context.Context, pluginCtx backend.PluginContext
|
||||
}
|
||||
instance, ok := i.(*datasourceInfo)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to cast datasource info")
|
||||
return nil, backend.DownstreamError(errors.New("failed to cast datasource info"))
|
||||
}
|
||||
return instance, nil
|
||||
}
|
||||
@@ -87,3 +87,11 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
|
||||
handler := httpadapter.New(s.registerResourceRoutes())
|
||||
return handler.CallResource(ctx, req, sender)
|
||||
}
|
||||
|
||||
func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
dsInfo, err := s.getDSInfo(ctx, req.PluginContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return queryData(ctx, dsInfo, req)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,10 @@ export class ZipkinDatasource extends DataSourceWithBackend<ZipkinQuery, ZipkinJ
|
||||
}
|
||||
|
||||
if (target.query) {
|
||||
const query = this.applyVariables(target, options.scopedVars);
|
||||
if (config.featureToggles.zipkinBackendMigration && !this.nodeGraph?.enabled) {
|
||||
return super.query(options);
|
||||
}
|
||||
const query = this.applyTemplateVariables(target, options.scopedVars);
|
||||
return this.request<ZipkinSpan[]>(`${apiPrefix}/trace/${encodeURIComponent(query.query)}`).pipe(
|
||||
map((res) => responseToDataQueryResponse(res, this.nodeGraph?.enabled))
|
||||
);
|
||||
@@ -99,12 +102,12 @@ export class ZipkinDatasource extends DataSourceWithBackend<ZipkinQuery, ZipkinJ
|
||||
return {
|
||||
...query,
|
||||
datasource: this.getRef(),
|
||||
...this.applyVariables(query, scopedVars),
|
||||
...this.applyTemplateVariables(query, scopedVars),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
applyVariables(query: ZipkinQuery, scopedVars: ScopedVars) {
|
||||
applyTemplateVariables(query: ZipkinQuery, scopedVars: ScopedVars) {
|
||||
const expandedQuery = { ...query };
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user