为数据源插件添加资源处理器
您可以为数据源后端添加资源处理器,以使用自己的数据源特定路由扩展 Grafana HTTP API。本指南解释了您可能想添加资源处理器的理由以及一些常见做法。
资源处理器的用途
数据源从后端检索数据的主要方式是通过 查询方法。但有时您的数据源需要按需请求数据;例如,在数据源的查询编辑器内自动提供自动完成功能。
资源处理器对于构建允许用户向数据源写入的控件面板也很有用。例如,您可以为更新物联网设备的状态添加资源处理器。
实现资源处理器接口
要将资源处理器添加到您的后端插件中,您需要实现 backend.CallResourceHandler
接口。
您可以在插件中通过两种方式实现这一功能,即使用 httpadapter
包 或在您的插件中手动实现。
使用 httpadapter
包
由 httpadapter
包提供的,是处理资源的推荐方法。此包支持使用 http.Handler
接口来处理资源调用,并以更 Go-agnostic 的方式响应用户请求,从而更易于支持多个路由和方法(GET、POST 等)。
使用 http.Handler
允许您使用 Go 内置的 ServeMux
功能或您首选的 HTTP 路由库(例如,gorilla/mux
)。
以下示例演示了使用 httpadapter
包、ServeMux
和 http.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
您可以通过Grafana HTTP API使用端点,http://<GRAFANA_HOSTNAME>:<PORT>/api/datasources/uid/<DATASOURCE_UID>/resources{/<RESOURCE>}
来访问资源。其中,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
类的getResource
和postResource
助手来查询您的资源。为了为您的组件提供一个更友好、更便捷的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
对象中访问数据源实例,并使用getNamespaces
向http://<GRAFANA_HOSTNAME>:<PORT>/api/datasources/uid/<DATASOURCE_UID>/resources/namespaces
发送HTTP GET请求
const namespaces = await props.datasource.getNamespaces();
更多示例
使用资源处理程序和httpadapter
包的一些其他示例