How to add a resource handler for your data source

If you have some more advanced needs/use case and/or want to use a more Go-agnostic approach for handling resources using regular http.Handler you can use a package provided by the grafana-plugin-sdk-go named httpadapter. This package provides support for handling resource calls using an http.Handler.

What’s interesting with using http.Handler is that you can use that with Go’s builtin router functionality called ServeMux or use your preferred HTTP router library, for example gorilla/mux to name one of the more popular ones.

Given above example of CallResource method we can do the same thing using httpadapter and ServeMux.

package mydatasource

import (
	"context"
	"net/http"

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

type MyDatasource struct {
	resourceHandler backend.CallResourceHandler
}

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

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

func (d *MyDatasource) handleNamespaces(rw http.ResponseWriter, req *http.Request) {
	_, err := rw.Write([]byte(`{ namespaces: ["ns-1", "ns-2"] }`))
	if err != nil {
		return
	}
	rw.WriteHeader(http.StatusOK)
}

func (d *MyDatasource) handleProjects(rw http.ResponseWriter, req *http.Request) {
	_, err := rw.Write([]byte(`{ projects: ["project-1", "project-2"] }`))
	if err != nil {
		return
	}
	rw.WriteHeader(http.StatusOK)
}

Using some other HTTP router library with above example should be straightforward replacing the use of ServeMux.

Some other examples of using the httpadapter package can be found for some of the builtin Grafana datasources:

What if you need access to backend.PluginContext?
Use the PluginConfigFromContext function.

func (d *MyDatasource) handleNamespaces(rw http.ResponseWriter, req *http.Request) {
	pCtx := httpadapter.PluginConfigFromContext(req.Context())

	bytes, err := json.Marshal(pCtx.User)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
	}
	
	_, err := rw.Write(bytes)
	if err != nil {
		return
	}
	rw.WriteHeader(http.StatusOK)
}
4 Likes