0390b5601e
Implements initial support for resources using v0.14.0 of SDK. Ref #21512
138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
package resource
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
|
|
)
|
|
|
|
// ResourceResponseWriter is an implementation of http.ResponseWriter that
|
|
// records its mutations for later inspection in tests.
|
|
type ResourceResponseWriter struct {
|
|
// Code is the HTTP response code set by WriteHeader.
|
|
//
|
|
// Note that if a Handler never calls WriteHeader or Write,
|
|
// this might end up being 0, rather than the implicit
|
|
// http.StatusOK. To get the implicit value, use the Result
|
|
// method.
|
|
Code int
|
|
|
|
// HeaderMap contains the headers explicitly set by the Handler.
|
|
// It is an internal detail.
|
|
//
|
|
// Deprecated: HeaderMap exists for historical compatibility
|
|
// and should not be used. To access the headers returned by a handler,
|
|
// use the Response.Header map as returned by the Result method.
|
|
HeaderMap http.Header
|
|
|
|
// Body is the buffer to which the Handler's Write calls are sent.
|
|
// If nil, the Writes are silently discarded.
|
|
Body *bytes.Buffer
|
|
|
|
// Flushed is whether the Handler called Flush.
|
|
Flushed bool
|
|
|
|
wroteHeader bool
|
|
}
|
|
|
|
// NewResourceResponseWriter returns an initialized ResponseWriter.
|
|
func NewResourceResponseWriter() *ResourceResponseWriter {
|
|
return &ResourceResponseWriter{
|
|
HeaderMap: make(http.Header),
|
|
Body: new(bytes.Buffer),
|
|
Code: 200,
|
|
}
|
|
}
|
|
|
|
// Header implements http.ResponseWriter. It returns the response
|
|
// headers to mutate within a handler. To test the headers that were
|
|
// written after a handler completes, use the Result method and see
|
|
// the returned Response value's Header.
|
|
func (rw *ResourceResponseWriter) Header() http.Header {
|
|
m := rw.HeaderMap
|
|
if m == nil {
|
|
m = make(http.Header)
|
|
rw.HeaderMap = m
|
|
}
|
|
return m
|
|
}
|
|
|
|
// writeHeader writes a header if it was not written yet and
|
|
// detects Content-Type if needed.
|
|
//
|
|
// bytes or str are the beginning of the response body.
|
|
// We pass both to avoid unnecessarily generate garbage
|
|
// in rw.WriteString which was created for performance reasons.
|
|
// Non-nil bytes win.
|
|
func (rw *ResourceResponseWriter) writeHeader(b []byte, str string) {
|
|
if rw.wroteHeader {
|
|
return
|
|
}
|
|
if len(str) > 512 {
|
|
str = str[:512]
|
|
}
|
|
|
|
m := rw.Header()
|
|
|
|
_, hasType := m["Content-Type"]
|
|
hasTE := m.Get("Transfer-Encoding") != ""
|
|
if !hasType && !hasTE {
|
|
if b == nil {
|
|
b = []byte(str)
|
|
}
|
|
m.Set("Content-Type", http.DetectContentType(b))
|
|
}
|
|
|
|
rw.WriteHeader(200)
|
|
}
|
|
|
|
// Write implements http.ResponseWriter. The data in buf is written to
|
|
// rw.Body, if not nil.
|
|
func (rw *ResourceResponseWriter) Write(buf []byte) (int, error) {
|
|
rw.writeHeader(buf, "")
|
|
if rw.Body != nil {
|
|
rw.Body.Write(buf)
|
|
}
|
|
return len(buf), nil
|
|
}
|
|
|
|
// WriteHeader implements http.ResponseWriter.
|
|
func (rw *ResourceResponseWriter) WriteHeader(code int) {
|
|
if rw.wroteHeader {
|
|
return
|
|
}
|
|
rw.Code = code
|
|
rw.wroteHeader = true
|
|
if rw.HeaderMap == nil {
|
|
rw.HeaderMap = make(http.Header)
|
|
}
|
|
}
|
|
|
|
// Flush implements http.Flusher.
|
|
func (rw *ResourceResponseWriter) Flush() {
|
|
if !rw.wroteHeader {
|
|
rw.WriteHeader(200)
|
|
}
|
|
}
|
|
|
|
// Result returns the response generated by the handler.
|
|
func (rw *ResourceResponseWriter) Result() *pluginv2.CallResource_Response {
|
|
res := &pluginv2.CallResource_Response{
|
|
Code: int32(rw.Code),
|
|
Headers: map[string]*pluginv2.CallResource_StringList{},
|
|
}
|
|
|
|
if rw.Body != nil {
|
|
res.Body = rw.Body.Bytes()
|
|
}
|
|
|
|
for key, values := range rw.Header().Clone() {
|
|
res.Headers[key] = &pluginv2.CallResource_StringList{
|
|
Values: values,
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|