feat: add sonarr support

This commit is contained in:
Dawid Wysokiński 2022-07-16 07:49:13 +02:00
parent 091b43c149
commit 08b899e620
Signed by: Kichiyaki
GPG Key ID: 1ECC5DE481BE5184
5 changed files with 147 additions and 0 deletions

View File

@ -7,12 +7,15 @@ type ErrorCode uint8
const (
ErrorCodeUnknown ErrorCode = iota
ErrorCodeValidation
ErrorCodeIO // External I/O error such as network failure.
)
func (e ErrorCode) String() string {
switch e {
case ErrorCodeValidation:
return "validation-error"
case ErrorCodeIO:
return "io-error"
case ErrorCodeUnknown:
fallthrough
default:

View File

@ -14,6 +14,7 @@ func TestErrorCode_String(t *testing.T) {
assert.Equal(t, domain.ErrorCodeUnknown.String(), "internal-server-error")
assert.Equal(t, domain.ErrorCodeValidation.String(), "validation-error")
assert.Equal(t, domain.ErrorCodeIO.String(), "io-error")
}
func TestError(t *testing.T) {
@ -45,6 +46,16 @@ func TestError(t *testing.T) {
expectedErr: nil,
expectedMsg: "err err",
},
{
name: "OK: WithCode(domain.ErrorCodeIO), WithMessage",
options: []domain.ErrorOption{
domain.WithCode(domain.ErrorCodeIO),
domain.WithMessage("err err"),
},
expectedCode: domain.ErrorCodeIO,
expectedErr: nil,
expectedMsg: "err err",
},
{
name: "OK: WithCode(domain.ErrorCodeUnknown), WithMessagef, WithErr",
options: []domain.ErrorOption{

View File

@ -41,6 +41,8 @@ func errorCodeToHTTPStatus(code domain.ErrorCode) int {
switch code {
case domain.ErrorCodeValidation:
return http.StatusBadRequest
case domain.ErrorCodeIO:
return http.StatusServiceUnavailable
case domain.ErrorCodeUnknown:
fallthrough
default:

View File

@ -2,7 +2,12 @@ package service
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/domain"
)
type Ntfy struct {
@ -24,5 +29,33 @@ func NewNtfy(client *http.Client, url string, topic string, username string, pas
}
func (n *Ntfy) Publish(ctx context.Context, title, message string) error {
// initialize request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, n.url+"/"+n.topic, strings.NewReader(message))
if err != nil {
return fmt.Errorf("http.NewRequestWithContext: %w", err)
}
// set required headers
req.Header.Set("Title", title)
if n.username != "" && n.password != "" {
req.SetBasicAuth(n.username, n.password)
}
// send request
resp, err := n.client.Do(req)
if err != nil {
return fmt.Errorf("client.Do: %w", err)
}
defer func() {
_ = resp.Body.Close()
}()
_, _ = io.Copy(io.Discard, resp.Body) // discard body, as it is not needed
if resp.StatusCode != http.StatusOK {
return domain.NewError(
domain.WithCode(domain.ErrorCodeIO),
domain.WithMessagef("ntfy returned unexpected status code: %d", resp.StatusCode),
)
}
return nil
}

View File

@ -0,0 +1,98 @@
package service_test
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/domain"
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/service"
"github.com/stretchr/testify/assert"
)
func TestNtfy_Publish(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
tests := []struct {
name string
topic string
title string
message string
username string
password string
}{
{
name: "without authorization",
topic: "topicc",
title: "title",
message: "msg",
username: "",
password: "",
},
{
name: "with authorization",
topic: "topicc",
title: "title",
message: "msg",
username: "uname",
password: "pwd",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path[1:] != tt.topic {
w.WriteHeader(http.StatusNotFound)
return
}
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if r.Header.Get("Title") != tt.title {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("invalid title"))
return
}
if uname, pwd, _ := r.BasicAuth(); uname != tt.username || pwd != tt.password {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("invalid username or password"))
return
}
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
err := service.NewNtfy(srv.Client(), srv.URL, tt.topic, tt.username, tt.password).
Publish(context.Background(), tt.title, tt.message)
assert.NoError(t, err)
})
}
})
t.Run("ERR: status code != 200", func(t *testing.T) {
t.Parallel()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}))
defer srv.Close()
err := service.NewNtfy(srv.Client(), srv.URL, "topic", "", "").
Publish(context.Background(), "title", "msg")
assert.ErrorIs(t, err, domain.NewError(
domain.WithCode(domain.ErrorCodeIO),
domain.WithMessagef("ntfy returned unexpected status code: %d", http.StatusNotImplemented),
))
})
}