跳到主内容

为数据源插件添加资源处理程序

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

资源处理程序的用途

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

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

实现资源处理程序接口

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

您可以通过两种方式在插件中实现此功能:使用 httpadapter手动实现 backend.CallResourceHandler

使用 httpadapter

Grafana Plugin 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 包的其他示例: