Plugins: Refactor call resource API handling (#67234)
Moving call resource API stream handling within plugin management as a utility/wrapper. Closes #66889 Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Browne
parent
b5fbce50b3
commit
4cbda914bd
@@ -0,0 +1,65 @@
|
||||
package httpresponsesender
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
)
|
||||
|
||||
// HTTPResponseSender implements backend.CallResourceResponseSender and
|
||||
// writes an HTTP response using an http.ResponseWriter given received
|
||||
// backend.CallResourceResponse(s).
|
||||
type HTTPResponseSender struct {
|
||||
processedStreams int
|
||||
w http.ResponseWriter
|
||||
}
|
||||
|
||||
// New creates a new HTTPResponseSender.
|
||||
func New(w http.ResponseWriter) *HTTPResponseSender {
|
||||
if w == nil {
|
||||
panic("response writer cannot be nil")
|
||||
}
|
||||
|
||||
return &HTTPResponseSender{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HTTPResponseSender) Send(resp *backend.CallResourceResponse) error {
|
||||
if resp == nil {
|
||||
return errors.New("resp cannot be nil")
|
||||
}
|
||||
|
||||
// Expected that headers and status are only part of first stream
|
||||
if s.processedStreams == 0 {
|
||||
for k, values := range resp.Headers {
|
||||
// Convert the keys to the canonical format of MIME headers.
|
||||
// This ensures that we can safely add/overwrite headers
|
||||
// even if the plugin returns them in non-canonical format
|
||||
// and be sure they won't be present multiple times in the response.
|
||||
k = textproto.CanonicalMIMEHeaderKey(k)
|
||||
|
||||
for _, v := range values {
|
||||
s.w.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
s.w.WriteHeader(resp.Status)
|
||||
}
|
||||
|
||||
if _, err := s.w.Write(resp.Body); err != nil {
|
||||
return fmt.Errorf("failed to write resource response: %v", err)
|
||||
}
|
||||
|
||||
if flusher, ok := s.w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
s.processedStreams++
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ backend.CallResourceResponseSender = &HTTPResponseSender{}
|
||||
@@ -0,0 +1,45 @@
|
||||
package httpresponsesender
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHTTPResponseSender(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
sender := New(w)
|
||||
require.NotNil(t, sender)
|
||||
|
||||
headers := http.Header{}
|
||||
headers.Add("X-Custom", "custom")
|
||||
err := sender.Send(&backend.CallResourceResponse{
|
||||
Status: http.StatusOK,
|
||||
Headers: headers,
|
||||
Body: []byte("Hello world"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
headers2 := http.Header{}
|
||||
headers2.Add("X-Custom-Two", "custom two")
|
||||
err = sender.Send(&backend.CallResourceResponse{
|
||||
Status: http.StatusNotFound,
|
||||
Headers: headers2,
|
||||
Body: []byte("Hello world again"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
resp := w.Result()
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
require.Equal(t, "custom", resp.Header.Get("X-Custom"))
|
||||
require.Empty(t, resp.Header.Get("X-Custom-Two"))
|
||||
bytes, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Hello worldHello world again", string(bytes))
|
||||
}
|
||||
Reference in New Issue
Block a user