WIP: Spawn backend plugins v2 (#19835)
* WIP: Spawn backend plugins v2 * Add test for plugin version * Fix support for SDK plugins Co-authored-by: Kyle Brandt <kyle@kbrandt.com> Co-authored-by: Marcus Olsson <olsson.e.marcus@gmail.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * WIP: Draft PR for fork of V2 sdk / bi-directional support (#19890) * temporary use export-datasource-plugin branch of grafana-plugin-sdk * fix failing test * remove debug (spew) lines * misc cleanup * add expressions feature toggle * use latest grafana-plugin-sdk-go
This commit is contained in:
committed by
Marcus Efraimsson
parent
b0198e7c9c
commit
fc08c26025
@@ -0,0 +1,176 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/grafana/grafana-plugin-sdk-go"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/dataframe"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/genproto/datasource"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
)
|
||||
|
||||
type grafanaAPI struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (s *grafanaAPI) QueryDatasource(ctx context.Context, req *datasource.QueryDatasourceRequest) (*datasource.QueryDatasourceResponse, error) {
|
||||
if len(req.Queries) == 0 {
|
||||
return nil, fmt.Errorf("zero queries found in datasource request")
|
||||
}
|
||||
getDsInfo := &models.GetDataSourceByIdQuery{
|
||||
Id: req.DatasourceId,
|
||||
OrgId: req.OrgId,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(getDsInfo); err != nil {
|
||||
return nil, fmt.Errorf("Could not find datasource %v", err)
|
||||
}
|
||||
|
||||
// Convert plugin-model (datasource) queries to tsdb queries
|
||||
queries := make([]*tsdb.Query, len(req.Queries))
|
||||
for i, query := range req.Queries {
|
||||
sj, err := simplejson.NewJson([]byte(query.ModelJson))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries[i] = &tsdb.Query{
|
||||
RefId: query.RefId,
|
||||
IntervalMs: query.IntervalMs,
|
||||
MaxDataPoints: query.MaxDataPoints,
|
||||
DataSource: getDsInfo.Result,
|
||||
Model: sj,
|
||||
}
|
||||
}
|
||||
|
||||
timeRange := tsdb.NewTimeRange(req.TimeRange.FromRaw, req.TimeRange.ToRaw)
|
||||
tQ := &tsdb.TsdbQuery{
|
||||
TimeRange: timeRange,
|
||||
Queries: queries,
|
||||
}
|
||||
|
||||
// Execute the converted queries
|
||||
tsdbRes, err := tsdb.HandleRequest(ctx, getDsInfo.Result, tQ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Convert tsdb results (map) to plugin-model/datasource (slice) results
|
||||
// Only error and Series responses mapped.
|
||||
results := make([]*datasource.QueryResult, len(tsdbRes.Results))
|
||||
resIdx := 0
|
||||
for refID, res := range tsdbRes.Results {
|
||||
qr := &datasource.QueryResult{
|
||||
RefId: refID,
|
||||
}
|
||||
if res.Error != nil {
|
||||
qr.Error = res.ErrorString
|
||||
results[resIdx] = qr
|
||||
resIdx++
|
||||
continue
|
||||
}
|
||||
|
||||
encodedFrames := make([][]byte, len(res.Series))
|
||||
for sIdx, series := range res.Series {
|
||||
frame, err := tsdb.SeriesToFrame(series)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encodedFrames[sIdx], err = dataframe.MarshalArrow(frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
qr.Dataframes = encodedFrames
|
||||
results[resIdx] = qr
|
||||
|
||||
resIdx++
|
||||
}
|
||||
return &datasource.QueryDatasourceResponse{Results: results}, nil
|
||||
}
|
||||
|
||||
func NewDatasourcePluginWrapperV2(log log.Logger, plugin sdk.DatasourcePlugin) *DatasourcePluginWrapperV2 {
|
||||
return &DatasourcePluginWrapperV2{DatasourcePlugin: plugin, logger: log}
|
||||
}
|
||||
|
||||
type DatasourcePluginWrapperV2 struct {
|
||||
sdk.DatasourcePlugin
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataSource, query *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
jsonData, err := ds.JsonData.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pbQuery := &datasource.DatasourceRequest{
|
||||
Datasource: &datasource.DatasourceInfo{
|
||||
Name: ds.Name,
|
||||
Type: ds.Type,
|
||||
Url: ds.Url,
|
||||
Id: ds.Id,
|
||||
OrgId: ds.OrgId,
|
||||
JsonData: string(jsonData),
|
||||
DecryptedSecureJsonData: ds.SecureJsonData.Decrypt(),
|
||||
},
|
||||
TimeRange: &datasource.TimeRange{
|
||||
FromRaw: query.TimeRange.From,
|
||||
ToRaw: query.TimeRange.To,
|
||||
ToEpochMs: query.TimeRange.GetToAsMsEpoch(),
|
||||
FromEpochMs: query.TimeRange.GetFromAsMsEpoch(),
|
||||
},
|
||||
Queries: []*datasource.Query{},
|
||||
}
|
||||
|
||||
for _, q := range query.Queries {
|
||||
modelJSON, err := q.Model.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbQuery.Queries = append(pbQuery.Queries, &datasource.Query{
|
||||
ModelJson: string(modelJSON),
|
||||
IntervalMs: q.IntervalMs,
|
||||
RefId: q.RefId,
|
||||
MaxDataPoints: q.MaxDataPoints,
|
||||
})
|
||||
}
|
||||
|
||||
pbres, err := tw.DatasourcePlugin.Query(ctx, pbQuery, &grafanaAPI{logger: tw.logger})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &tsdb.Response{
|
||||
Results: map[string]*tsdb.QueryResult{},
|
||||
}
|
||||
|
||||
for _, r := range pbres.Results {
|
||||
qr := &tsdb.QueryResult{
|
||||
RefId: r.RefId,
|
||||
}
|
||||
|
||||
if r.Error != "" {
|
||||
qr.Error = errors.New(r.Error)
|
||||
qr.ErrorString = r.Error
|
||||
}
|
||||
|
||||
if r.MetaJson != "" {
|
||||
metaJSON, err := simplejson.NewJson([]byte(r.MetaJson))
|
||||
if err != nil {
|
||||
tw.logger.Error("Error parsing JSON Meta field: " + err.Error())
|
||||
}
|
||||
qr.Meta = metaJSON
|
||||
}
|
||||
qr.Dataframes = r.Dataframes
|
||||
|
||||
res.Results[r.RefId] = qr
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
Reference in New Issue
Block a user