跳至主要内容

为数据源插件添加资源处理器

您可以向数据源后端添加资源处理器,以使用您自己的数据源特定路由扩展 Grafana HTTP API。本指南解释了您可能想要添加 资源 处理器的原因以及一些常见的方法。

资源处理器的用途

数据源从后端检索数据的主要方式是通过 查询方法。但有时您的数据源需要按需请求数据;例如,在数据源的查询编辑器中自动提供自动完成功能。

资源处理器对于构建允许用户写回数据源的控制面板也很有用。例如,您可以添加一个资源处理器来更新 IoT 设备的状态。

实现资源处理器接口

要向您的后端插件添加资源处理器,您需要实现 backend.CallResourceHandler 接口。

您可以在插件中以两种方式实现此功能,使用 httpadapter手动实现 在您的插件中。

使用 httpadapter

Grafana 插件 SDK for Go 提供的 httpadapter 包是处理资源的推荐方法。此包提供对使用 http.Handler 接口处理资源调用的支持,并允许以更与 Go 无关的方式响应 HTTP 请求,并使其更容易支持多个路由和方法(GET、POST 等)。

使用 http.Handler 还允许您使用 Go 的内置路由功能,称为 ServeMux 或您首选的 HTTP 路由器库(例如,gorilla/mux)。

注意

Go 1.22 包含路由增强功能,该功能使用 ServeMux 添加了对方法匹配和通配符的支持。

在以下示例中,我们演示了如何使用 httpadapter 包、ServeMuxhttp.Handler 来添加对检索命名空间 (/namespaces)、项目 (/projects) 和更新某些设备 (/device) 状态的支持。

package myplugin

import (
"context"
"net/http"

"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
)

type MyPlugin struct {
resourceHandler backend.CallResourceHandler
}

func New() *MyPlugin {
p := &MyPlugin{}
mux := http.NewServeMux()
mux.HandleFunc("/namespaces", p.handleNamespaces)
mux.HandleFunc("/projects", p.handleProjects)
p.resourceHandler := httpadapter.New(mux)
return p
}

func (p *MyPlugin) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
return p.resourceHandler.CallResource(ctx, req, sender)
}

func (p *MyPlugin) handleNamespaces(rw http.ResponseWriter, req *http.Request) {
rw.Header().Add("Content-Type", "application/json")
_, err := rw.Write([]byte(`{ "namespaces": ["ns-1", "ns-2"] }`))
if err != nil {
return
}
rw.WriteHeader(http.StatusOK)
}

func (p *MyPlugin) handleProjects(rw http.ResponseWriter, req *http.Request) {
rw.Header().Add("Content-Type", "application/json")
_, err := rw.Write([]byte(`{ "projects": ["project-1", "project-2"] }`))
if err != nil {
return
}
rw.WriteHeader(http.StatusOK)
}

访问后端插件上下文

您可以使用 backend.PluginConfigFromContext 函数访问 backend.PluginContext。它保存有关插件请求的上下文信息,例如执行请求的用户。

func (p *MyPlugin) handleSomeRoute(rw http.ResponseWriter, req *http.Request) {
pCtx := backend.PluginConfigFromContext(req.Context())
bytes, err := json.Marshal(pCtx.User)
if err != nil {
return
}

rw.Header().Add("Content-Type", "application/json")
_, err := rw.Write(bytes)
if err != nil {
return
}
rw.WriteHeader(http.StatusOK)
}

手动实现 backend.CallResourceHandler

手动实现 backend.CallResourceHandler 接口可能足以满足基本需求。要支持几个不同的检索数据的路由,您可以使用带有 req.Path 的 switch 语句。

func (p *MyPlugin) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
switch req.Path {
case "namespaces":
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Body: []byte(`{ "namespaces": ["ns-1", "ns-2"] }`),
})
case "projects":
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Body: []byte(`{ "projects": ["project-1", "project-2"] }`),
})
default:
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusNotFound,
})
}
}

访问数据源资源

实现后,您可以使用 Grafana HTTP API 和前端访问资源。

使用 Grafana HTTP API

您可以通过使用端点 http://<GRAFANA_HOSTNAME>:<PORT>/api/datasources/uid/<DATASOURCE_UID>/resources{/<RESOURCE>} 通过 Grafana HTTP API 访问资源。DATASOURCE_UID 是唯一标识您的数据源的数据源唯一标识符 (UID),RESOURCE 取决于资源处理器的实现方式以及支持哪些资源(路由)。

使用上述示例,您可以访问以下资源:

  • HTTP GET http://<GRAFANA_HOSTNAME>:<PORT>/api/datasources/uid/<DATASOURCE_UID>/resources/namespaces
  • HTTP GET http://<GRAFANA_HOSTNAME>:<PORT>/api/datasources/uid/<DATASOURCE_UID>/resources/projects
提示

要验证数据源 UID,您可以在浏览器的开发者工具控制台中输入 window.grafanaBootData.settings.datasources,以列出 Grafana 实例中所有配置的数据源。

从前端

您可以使用 DataSourceWithBackend 类的 getResourcepostResource 帮助程序查询您的资源。为了为您的组件提供更友好、更方便的 API,建议您使用以下示例中所示的函数扩展您的数据源类和实例,以用于每个路由。

export class MyDataSource extends DataSourceWithBackend<MyQuery, MyDataSourceOptions> {
constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
super(instanceSettings);
}

getNamespaces(): Promise<NamespacesResponse> {
return this.getResource('/namespaces');
}

getProjects(): Promise<ProjectsResponse> {
return this.getResource('/projects');
}
}

例如,在查询编辑器组件中,您可以从 props 对象访问数据源实例,并使用 getNamespaceshttp://<GRAFANA_HOSTNAME>:<PORT>/api/datasources/uid/<DATASOURCE_UID>/resources/namespaces 发送 HTTP GET 请求。

const namespaces = await props.datasource.getNamespaces();

其他示例

使用资源处理器和 httpadapter 包的其他一些示例。