Add Panic Recovery & Logging to Client JSON Unmarshalling (#139)

This commit is contained in:
Russell Troxel 2023-03-28 11:33:13 -07:00 committed by GitHub
parent fed7319e77
commit 73fbf8dfaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 1 deletions

View File

@ -4,8 +4,10 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/onedr0p/exportarr/internal/config"
"go.uber.org/zap"
@ -62,6 +64,30 @@ func NewClient(config *config.Config) (*Client, error) {
}, nil
}
func (c *Client) unmarshalBody(b io.Reader, target interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
// return recovered panic as error
err = fmt.Errorf("Recovered from panic: %s", r)
log := zap.S()
if zap.S().Level() == zap.DebugLevel {
s := new(strings.Builder)
_, copyErr := io.Copy(s, b)
if copyErr != nil {
zap.S().Errorw("Failed to copy body to string in recover",
"error", err, "recover", r)
}
log = log.With("body", s.String())
}
log.Errorw("Recovered while unmarshalling response", "error", r)
}
}()
err = json.NewDecoder(b).Decode(target)
return
}
// DoRequest - Take a HTTP Request and return Unmarshaled data
func (c *Client) DoRequest(endpoint string, target interface{}, queryParams ...map[string]string) error {
values := c.URL.Query()
@ -84,5 +110,5 @@ func (c *Client) DoRequest(endpoint string, target interface{}, queryParams ...m
return fmt.Errorf("Failed to execute HTTP Request(%s): %w", url, err)
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(target)
return c.unmarshalBody(resp.Body, target)
}

View File

@ -1,6 +1,7 @@
package client
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
@ -79,3 +80,36 @@ func TestDoRequest(t *testing.T) {
})
}
}
func TestDoRequest_PanicRecovery(t *testing.T) {
require := require.New(t)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ret := struct {
TestField string
TestField2 string
}{
TestField: "asdf",
TestField2: "asdf2",
}
s, err := json.Marshal(ret)
require.NoError(err)
w.Write(s)
w.WriteHeader(http.StatusOK)
return
}))
defer ts.Close()
c := &config.Config{
URL: ts.URL,
ApiVersion: "v3",
}
client, err := NewClient(c)
require.Nil(err, "NewClient should not return an error")
require.NotNil(client, "NewClient should return a client")
err = client.DoRequest("test", nil)
require.NotPanics(func() {
require.Error(err, "DoRequest should return an error: %s", err)
}, "DoRequest should recover from a panic")
}