feat: add radarr support #9
|
@ -18,6 +18,8 @@ func TestErrorCode_String(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
errToWrap := errors.New("WithErr")
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package domain
|
||||
|
||||
type RadarrEventType string
|
||||
|
||||
const (
|
||||
RadarrEventTypeDownload RadarrEventType = "Download"
|
||||
RadarrEventTypeTest RadarrEventType = "Test"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUnsupportedRadarrEventType = NewError(WithCode(ErrorCodeValidation), WithMessage("unsupported event type"))
|
||||
)
|
||||
|
||||
func NewRadarrEventType(s string) (RadarrEventType, error) {
|
||||
conv := RadarrEventType(s)
|
||||
switch conv {
|
||||
case RadarrEventTypeDownload,
|
||||
RadarrEventTypeTest:
|
||||
return conv, nil
|
||||
default:
|
||||
return "", ErrUnsupportedSonarrEventType
|
||||
}
|
||||
}
|
||||
|
||||
func (r RadarrEventType) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
type RadarrMovie struct {
|
||||
ID int64
|
||||
Title string
|
||||
ReleaseDate string
|
||||
}
|
||||
|
||||
type RadarrWebhookPayload struct {
|
||||
EventType RadarrEventType
|
||||
Movie RadarrMovie
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/domain"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewRadarrEventType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
events := []domain.RadarrEventType{domain.RadarrEventTypeDownload, domain.RadarrEventTypeTest}
|
||||
|
||||
for _, ev := range events {
|
||||
res, err := domain.NewRadarrEventType(ev.String())
|
||||
assert.Equal(t, ev, res)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ERR: invalid event type", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
events := []string{"test1", "test2", "aaaa", "bbb"}
|
||||
|
||||
for _, ev := range events {
|
||||
res, err := domain.NewRadarrEventType(ev)
|
||||
assert.Zero(t, res)
|
||||
assert.ErrorIs(t, err, domain.ErrUnsupportedRadarrEventType)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -12,7 +12,9 @@ func TestNewSonarrEventType(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
events := []domain.SonarrEventType{domain.SonarrEventTypeDownload}
|
||||
t.Parallel()
|
||||
|
||||
events := []domain.SonarrEventType{domain.SonarrEventTypeDownload, domain.SonarrEventTypeTest}
|
||||
|
||||
for _, ev := range events {
|
||||
res, err := domain.NewSonarrEventType(ev.String())
|
||||
|
@ -22,6 +24,8 @@ func TestNewSonarrEventType(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("ERR: invalid event type", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
events := []string{"test1", "test2", "aaaa", "bbb"}
|
||||
|
||||
for _, ev := range events {
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/domain"
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/rest"
|
||||
)
|
||||
|
||||
type FakeRadarrService struct {
|
||||
ProcessStub func(context.Context, domain.RadarrWebhookPayload) error
|
||||
processMutex sync.RWMutex
|
||||
processArgsForCall []struct {
|
||||
arg1 context.Context
|
||||
arg2 domain.RadarrWebhookPayload
|
||||
}
|
||||
processReturns struct {
|
||||
result1 error
|
||||
}
|
||||
processReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) Process(arg1 context.Context, arg2 domain.RadarrWebhookPayload) error {
|
||||
fake.processMutex.Lock()
|
||||
ret, specificReturn := fake.processReturnsOnCall[len(fake.processArgsForCall)]
|
||||
fake.processArgsForCall = append(fake.processArgsForCall, struct {
|
||||
arg1 context.Context
|
||||
arg2 domain.RadarrWebhookPayload
|
||||
}{arg1, arg2})
|
||||
stub := fake.ProcessStub
|
||||
fakeReturns := fake.processReturns
|
||||
fake.recordInvocation("Process", []interface{}{arg1, arg2})
|
||||
fake.processMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) ProcessCallCount() int {
|
||||
fake.processMutex.RLock()
|
||||
defer fake.processMutex.RUnlock()
|
||||
return len(fake.processArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) ProcessCalls(stub func(context.Context, domain.RadarrWebhookPayload) error) {
|
||||
fake.processMutex.Lock()
|
||||
defer fake.processMutex.Unlock()
|
||||
fake.ProcessStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) ProcessArgsForCall(i int) (context.Context, domain.RadarrWebhookPayload) {
|
||||
fake.processMutex.RLock()
|
||||
defer fake.processMutex.RUnlock()
|
||||
argsForCall := fake.processArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) ProcessReturns(result1 error) {
|
||||
fake.processMutex.Lock()
|
||||
defer fake.processMutex.Unlock()
|
||||
fake.ProcessStub = nil
|
||||
fake.processReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) ProcessReturnsOnCall(i int, result1 error) {
|
||||
fake.processMutex.Lock()
|
||||
defer fake.processMutex.Unlock()
|
||||
fake.ProcessStub = nil
|
||||
if fake.processReturnsOnCall == nil {
|
||||
fake.processReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.processReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.processMutex.RLock()
|
||||
defer fake.processMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeRadarrService) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ rest.RadarrService = new(FakeRadarrService)
|
|
@ -16,18 +16,26 @@ type SonarrService interface {
|
|||
Process(ctx context.Context, payload domain.SonarrWebhookPayload) error
|
||||
}
|
||||
|
||||
type WebhookHandler struct {
|
||||
sonarrSvc SonarrService
|
||||
//counterfeiter:generate -o internal/mock/radarr_service.gen.go . RadarrService
|
||||
type RadarrService interface {
|
||||
Process(ctx context.Context, payload domain.RadarrWebhookPayload) error
|
||||
}
|
||||
|
||||
func NewWebhookHandler(sonarrSvc SonarrService) *WebhookHandler {
|
||||
type WebhookHandler struct {
|
||||
sonarrSvc SonarrService
|
||||
radarrSvc RadarrService
|
||||
}
|
||||
|
||||
func NewWebhookHandler(sonarrSvc SonarrService, radarrSvc RadarrService) *WebhookHandler {
|
||||
return &WebhookHandler{
|
||||
sonarrSvc: sonarrSvc,
|
||||
radarrSvc: radarrSvc,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) Register(r chi.Router) {
|
||||
r.Post("/webhook/sonarr", h.handleSonarrWebhook)
|
||||
r.Post("/webhook/radarr", h.handleRadarrWebhook)
|
||||
}
|
||||
|
||||
type SonarrSeries struct {
|
||||
|
@ -90,3 +98,48 @@ func (h *WebhookHandler) handleSonarrWebhook(w http.ResponseWriter, r *http.Requ
|
|||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
type RadarrMovie struct {
|
||||
ID int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
ReleaseDate string `json:"releaseDate"`
|
||||
}
|
||||
|
||||
type RadarrWebhookRequest struct {
|
||||
EventType string `json:"eventType"`
|
||||
Movie RadarrMovie `json:"movie"`
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) handleRadarrWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
var req RadarrWebhookRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
renderErrorResponse(w, domain.NewError(
|
||||
domain.WithErr(err),
|
||||
domain.WithCode(domain.ErrorCodeValidation),
|
||||
domain.WithMessage("invalid request body"),
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
eventType, err := domain.NewRadarrEventType(req.EventType)
|
||||
if err != nil {
|
||||
renderErrorResponse(w, fmt.Errorf("domain.NewRadarrEventType: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
payload := domain.RadarrWebhookPayload{
|
||||
EventType: eventType,
|
||||
Movie: domain.RadarrMovie{
|
||||
ID: req.Movie.ID,
|
||||
Title: req.Movie.Title,
|
||||
ReleaseDate: req.Movie.ReleaseDate,
|
||||
},
|
||||
}
|
||||
|
||||
if err := h.radarrSvc.Process(r.Context(), payload); err != nil {
|
||||
renderErrorResponse(w, fmt.Errorf("RadarrService.Process: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ func TestWebhookHandler_Sonarr(t *testing.T) {
|
|||
tt.setup(&svc)
|
||||
}
|
||||
router := chi.NewRouter()
|
||||
rest.NewWebhookHandler(&svc).Register(router)
|
||||
rest.NewWebhookHandler(&svc, nil).Register(router)
|
||||
|
||||
resp := doRequest(router, http.MethodPost, "/webhook/sonarr", tt.body)
|
||||
defer assert.NoError(t, resp.Body.Close())
|
||||
|
@ -129,3 +129,102 @@ func TestWebhookHandler_Sonarr(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookHandler_Radarr(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func(svc *mock.FakeRadarrService)
|
||||
body io.Reader
|
||||
expectedStatus int
|
||||
target any
|
||||
expectedResponse any
|
||||
}{
|
||||
{
|
||||
name: "OK: event type=Download",
|
||||
setup: func(svc *mock.FakeRadarrService) {
|
||||
svc.ProcessReturns(nil)
|
||||
},
|
||||
body: func() *bytes.Buffer {
|
||||
var buf bytes.Buffer
|
||||
_ = json.NewEncoder(&buf).Encode(rest.RadarrWebhookRequest{
|
||||
EventType: "Download",
|
||||
Movie: rest.RadarrMovie{
|
||||
ID: 111,
|
||||
Title: "Title 1",
|
||||
ReleaseDate: "1970-01-01",
|
||||
},
|
||||
})
|
||||
return &buf
|
||||
}(),
|
||||
expectedStatus: http.StatusNoContent,
|
||||
},
|
||||
{
|
||||
name: "ERR: only JSON is accepted",
|
||||
setup: func(svc *mock.FakeRadarrService) {},
|
||||
body: func() *bytes.Buffer {
|
||||
var buf bytes.Buffer
|
||||
_ = gob.NewEncoder(&buf).Encode(rest.RadarrWebhookRequest{
|
||||
EventType: "Download",
|
||||
Movie: rest.RadarrMovie{
|
||||
ID: 111,
|
||||
Title: "Title 1",
|
||||
ReleaseDate: "1970-01-01",
|
||||
},
|
||||
})
|
||||
return &buf
|
||||
}(),
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
target: &rest.ErrorResponse{},
|
||||
expectedResponse: &rest.ErrorResponse{
|
||||
Error: rest.APIError{
|
||||
Code: domain.ErrorCodeValidation.String(),
|
||||
Message: "invalid request body",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: unsupported event type",
|
||||
setup: func(svc *mock.FakeRadarrService) {},
|
||||
body: func() *bytes.Buffer {
|
||||
var buf bytes.Buffer
|
||||
_ = json.NewEncoder(&buf).Encode(rest.RadarrWebhookRequest{
|
||||
EventType: "xxxx",
|
||||
})
|
||||
return &buf
|
||||
}(),
|
||||
expectedStatus: http.StatusBadRequest,
|
||||
target: &rest.ErrorResponse{},
|
||||
expectedResponse: &rest.ErrorResponse{
|
||||
Error: rest.APIError{
|
||||
Code: domain.ErrUnsupportedRadarrEventType.Code().String(),
|
||||
Message: domain.ErrUnsupportedRadarrEventType.Message(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := mock.FakeRadarrService{}
|
||||
if tt.setup != nil {
|
||||
tt.setup(&svc)
|
||||
}
|
||||
router := chi.NewRouter()
|
||||
rest.NewWebhookHandler(nil, &svc).Register(router)
|
||||
|
||||
resp := doRequest(router, http.MethodPost, "/webhook/radarr", tt.body)
|
||||
defer assert.NoError(t, resp.Body.Close())
|
||||
if tt.target != nil && tt.expectedResponse != nil {
|
||||
assertJSONResponse(t, resp, tt.expectedStatus, tt.expectedResponse, tt.target)
|
||||
} else {
|
||||
assert.Equal(t, tt.expectedStatus, resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/domain"
|
||||
)
|
||||
|
||||
type Radarr struct {
|
||||
publisher Publisher
|
||||
}
|
||||
|
||||
func NewRadarr(publisher Publisher) *Radarr {
|
||||
return &Radarr{
|
||||
publisher: publisher,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Radarr) Process(ctx context.Context, payload domain.RadarrWebhookPayload) error {
|
||||
title, err := s.buildTitle(payload.EventType, payload.Movie)
|
||||
if err != nil {
|
||||
return fmt.Errorf("buildTitle: %w", err)
|
||||
}
|
||||
|
||||
msg, err := s.buildMessage(payload.EventType, payload.Movie)
|
||||
if err != nil {
|
||||
return fmt.Errorf("buildMessage: %w", err)
|
||||
}
|
||||
|
||||
if err := s.publisher.Publish(ctx, title, msg); err != nil {
|
||||
return fmt.Errorf("Publisher.Publish: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Radarr) buildTitle(evType domain.RadarrEventType, _ domain.RadarrMovie) (string, error) {
|
||||
switch evType {
|
||||
case domain.RadarrEventTypeDownload,
|
||||
domain.RadarrEventTypeTest:
|
||||
return "New movie available (Radarr)", nil
|
||||
default:
|
||||
return "", domain.ErrUnsupportedRadarrEventType
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Radarr) buildMessage(evType domain.RadarrEventType, movie domain.RadarrMovie) (string, error) {
|
||||
switch evType {
|
||||
case domain.RadarrEventTypeDownload,
|
||||
domain.RadarrEventTypeTest:
|
||||
return movie.Title, nil
|
||||
default:
|
||||
return "", domain.ErrUnsupportedRadarrEventType
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package service_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/domain"
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/service"
|
||||
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/service/internal/mock"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRadarr_Process(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, evType := range [...]domain.RadarrEventType{
|
||||
domain.RadarrEventTypeDownload,
|
||||
domain.RadarrEventTypeTest,
|
||||
} {
|
||||
t.Run("event type="+evType.String(), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
publisher := &mock.FakePublisher{}
|
||||
publisher.PublishReturns(nil)
|
||||
|
||||
payload := generateRadarrWebhookPayload(evType)
|
||||
|
||||
err := service.NewRadarr(publisher).Process(context.Background(), payload)
|
||||
assert.NoError(t, err)
|
||||
_, title, msg := publisher.PublishArgsForCall(0)
|
||||
assert.Equal(t, "New movie available (Radarr)", title)
|
||||
assert.Equal(t, payload.Movie.Title, msg)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func generateRadarrWebhookPayload(ev domain.RadarrEventType) domain.RadarrWebhookPayload {
|
||||
s := rand.NewSource(time.Now().UnixNano())
|
||||
r := rand.New(s)
|
||||
|
||||
return domain.RadarrWebhookPayload{
|
||||
EventType: ev,
|
||||
Movie: domain.RadarrMovie{
|
||||
ID: r.Int63(),
|
||||
Title: uuid.NewString(),
|
||||
ReleaseDate: "1970-01-01",
|
||||
},
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ func (s *Sonarr) buildTitle(evType domain.SonarrEventType, series domain.SonarrS
|
|||
switch evType {
|
||||
case domain.SonarrEventTypeDownload,
|
||||
domain.SonarrEventTypeTest:
|
||||
return series.Title + " - New episode (Sonarr)", nil
|
||||
return series.Title + " - New episode available (Sonarr)", nil
|
||||
default:
|
||||
return "", domain.ErrUnsupportedSonarrEventType
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestSonarr_Process(t *testing.T) {
|
|||
require.Equal(t, len(payload.Episodes), publisher.PublishCallCount())
|
||||
for i, ep := range payload.Episodes {
|
||||
_, title, msg := publisher.PublishArgsForCall(i)
|
||||
assert.Equal(t, payload.Series.Title+" - New episode (Sonarr)", title)
|
||||
assert.Equal(t, payload.Series.Title+" - New episode available (Sonarr)", title)
|
||||
assert.Equal(t, fmt.Sprintf("S%d.E%d %s", ep.SeasonNumber, ep.EpisodeNumber, ep.Title), msg)
|
||||
}
|
||||
})
|
||||
|
|
48
main.go
48
main.go
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
@ -71,6 +70,11 @@ func newRouter() (*chi.Mux, error) {
|
|||
return nil, fmt.Errorf("newSonarrService: %w", err)
|
||||
}
|
||||
|
||||
radarrSvc, err := newRadarrService()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("newRadarrService: %w", err)
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Use(
|
||||
middleware.RealIP,
|
||||
|
@ -81,39 +85,53 @@ func newRouter() (*chi.Mux, error) {
|
|||
middleware.Recoverer,
|
||||
middleware.Heartbeat("/health"),
|
||||
)
|
||||
rest.NewWebhookHandler(sonarrSvc).Register(r)
|
||||
rest.NewWebhookHandler(sonarrSvc, radarrSvc).Register(r)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func newRadarrService() (*service.Radarr, error) {
|
||||
ntfy, err := newNtfyService("RADARR")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("newNtfyService: %w", err)
|
||||
}
|
||||
return service.NewRadarr(ntfy), nil
|
||||
}
|
||||
|
||||
func newSonarrService() (*service.Sonarr, error) {
|
||||
url := os.Getenv("SONARR_NTFY_URL")
|
||||
ntfy, err := newNtfyService("SONARR")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("newNtfyService: %w", err)
|
||||
}
|
||||
return service.NewSonarr(ntfy), nil
|
||||
}
|
||||
|
||||
func newNtfyService(envPrefix string) (*service.Ntfy, error) {
|
||||
url := os.Getenv(envPrefix + "_NTFY_URL")
|
||||
if url == "" {
|
||||
url = ntfyDefaultUrl
|
||||
}
|
||||
|
||||
topic := os.Getenv("SONARR_NTFY_TOPIC")
|
||||
topic := os.Getenv(envPrefix + "_NTFY_TOPIC")
|
||||
if topic == "" {
|
||||
return nil, errors.New(`env "SONARR_NTFY_TOPIC" is required`)
|
||||
return nil, fmt.Errorf(`env "%s_NTFY_TOPIC" is required`, envPrefix)
|
||||
}
|
||||
|
||||
timeout := os.Getenv("SONARR_NTFY_REQ_TIMEOUT")
|
||||
timeout := os.Getenv(envPrefix + "_NTFY_REQ_TIMEOUT")
|
||||
parsedTimeout := publisherDefaultTimeout
|
||||
var err error
|
||||
if timeout != "" {
|
||||
parsedTimeout, err = time.ParseDuration(timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`os.Getenv("SONARR_NTFY_REQ_TIMEOUT"): %w`, err)
|
||||
return nil, fmt.Errorf(`os.Getenv("%s_NTFY_REQ_TIMEOUT"): %w`, envPrefix, err)
|
||||
}
|
||||
|
||||
return service.NewSonarr(
|
||||
service.NewNtfy(
|
||||
&http.Client{Timeout: parsedTimeout},
|
||||
url,
|
||||
topic,
|
||||
os.Getenv("SONARR_NTFY_USERNAME"),
|
||||
os.Getenv("SONARR_NTFY_PASSWORD"),
|
||||
),
|
||||
return service.NewNtfy(
|
||||
&http.Client{Timeout: parsedTimeout},
|
||||
url,
|
||||
topic,
|
||||
os.Getenv(envPrefix+"_NTFY_USERNAME"),
|
||||
os.Getenv(envPrefix+"_NTFY_PASSWORD"),
|
||||
), nil
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue