diff --git a/README.md b/README.md index 45e11de..331b293 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Here are the currently supported endpoints: - [x] Calendar - [ ] Command -- [ ] Diskspace +- [x] Diskspace - [ ] History - [ ] Movie - [x] Returns all Movies in your collection @@ -25,6 +25,7 @@ Here are the currently supported endpoints: - [ ] Delete the movie with the given ID - [ ] Movie Lookup - [ ] Queue +- [ ] List Exclusions - [x] System-Status ## Getting started diff --git a/client.go b/client.go index 71d4d6d..f5c2fcd 100644 --- a/client.go +++ b/client.go @@ -1,7 +1,7 @@ package radarr import ( - "fmt" + "errors" "net/http" "net/url" "time" @@ -17,11 +17,10 @@ type HTTPClientInterface interface { func New(radarrURL, apiKey string, client HTTPClientInterface) (*Service, error) { valid, err := url.ParseRequestURI(radarrURL) if err != nil { - return nil, fmt.Errorf("Please specify a valid URL") + return nil, errors.New("Please specify a valid URL") } // if client not specified, defaulting to default http client - // TODO: test it if client == nil { d := http.DefaultClient d.Transport = newTransport() @@ -32,6 +31,7 @@ func New(radarrURL, apiKey string, client HTTPClientInterface) (*Service, error) s := &Service{client: client, url: valid.String(), apiKey: apiKey} s.Movies = newMovieService(s) s.SystemStatus = newSystemStatusService(s) + s.Diskspace = newDiskspaceService(s) return s, nil } @@ -42,6 +42,14 @@ type Service struct { url string // Radarr base URL apiKey string - Movies *MovieService + // https://github.com/Radarr/Radarr/wiki/API:Calendar + // https://github.com/Radarr/Radarr/wiki/API:Movie + // https://github.com/Radarr/Radarr/wiki/API:Movie-Lookup + Movies *MovieService + + // https://github.com/Radarr/Radarr/wiki/API:System-Status SystemStatus *SystemStatusService + + // https://github.com/Radarr/Radarr/wiki/API:Diskspace + Diskspace *DiskspaceService } diff --git a/constants.go b/constants.go index dc0dbce..43f589a 100644 --- a/constants.go +++ b/constants.go @@ -1,7 +1,8 @@ package radarr const ( - movieURI string = "/movie" - statusURI string = "/system/status" - upcomingURI string = "/calendar" + movieURI string = "/movie" + statusURI string = "/system/status" + upcomingURI string = "/calendar" + diskspaceURI string = "/diskspace" ) diff --git a/disk.go b/disk.go new file mode 100644 index 0000000..1b6f5f9 --- /dev/null +++ b/disk.go @@ -0,0 +1,51 @@ +package radarr + +import ( + "encoding/json" + "fmt" +) + +// Diskspace disk space Radarr response +type Diskspace struct { + Path string `json:"path"` + Label string `json:"label"` + FreeSpace int64 `json:"freeSpace"` + TotalSpace int64 `json:"totalSpace"` +} + +// Diskspaces describe disk space info on your Radarr instance +type Diskspaces []Diskspace + +// DiskspaceService contains Radarr diskspace operations +type DiskspaceService struct { + s *Service +} + +func newDiskspaceService(s *Service) *DiskspaceService { + return &DiskspaceService{ + s: s, + } +} + +// Get return Radarr disk space info +func (s *DiskspaceService) Get() (*Diskspaces, error) { + diskspaceURL := fmt.Sprintf("%s/api%s?apikey=%s", s.s.url, diskspaceURI, s.s.apiKey) + response, err := s.s.client.Get(diskspaceURL) + if err != nil { + return nil, err + } + + err = parseRadarrResponse(response) + if err != nil { + return nil, err + } + + var diskspaces Diskspaces + err = json.NewDecoder(response.Body).Decode(&diskspaces) + if err != nil { + return nil, err + } + + _ = response.Body.Close() + return &diskspaces, nil +} diff --git a/disk_test.go b/disk_test.go new file mode 100644 index 0000000..7e8f765 --- /dev/null +++ b/disk_test.go @@ -0,0 +1,108 @@ +package radarr + +import ( + "net/http" + "reflect" + "testing" + + internal "github.com/SkYNewZ/radarr/internal/radarr" +) + +func Test_newDiskspaceService(t *testing.T) { + type args struct { + s *Service + } + + s := &Service{client: http.DefaultClient, apiKey: internal.DummyAPIKey, url: internal.DummyURL} + tests := []struct { + name string + args args + want *DiskspaceService + }{ + struct { + name string + args args + want *DiskspaceService + }{ + name: "Constructor", + args: args{s}, + want: &DiskspaceService{s}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newDiskspaceService(tt.args.s); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newDiskspaceService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDiskspaceService_Get(t *testing.T) { + type fields struct { + s *Service + } + + var classicService *Service = &Service{client: internal.DummyHTTPClient, url: internal.DummyURL, apiKey: internal.DummyAPIKey} + var badAPIKeyService *Service = &Service{client: internal.DummyHTTPClient, url: internal.DummyURL, apiKey: "bad"} + + var exepectedResponse *Diskspaces = &Diskspaces{ + Diskspace{ + Label: "/", + Path: "/", + FreeSpace: 11059175424, + TotalSpace: 15614754816, + }, + Diskspace{ + Label: "/home", + Path: "/home", + FreeSpace: 88775757824, + TotalSpace: 98325770240, + }, + } + + tests := []struct { + name string + fields fields + want *Diskspaces + wantErr bool + }{ + struct { + name string + fields fields + want *Diskspaces + wantErr bool + }{ + name: "Diskspace lengh should be 2", + fields: fields{classicService}, + wantErr: false, + want: exepectedResponse, + }, + struct { + name string + fields fields + want *Diskspaces + wantErr bool + }{ + name: "Bad API Key", + wantErr: true, + fields: fields{badAPIKeyService}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &DiskspaceService{ + s: tt.fields.s, + } + got, err := s.Get() + if (err != nil) != tt.wantErr { + t.Errorf("DiskspaceService.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("DiskspaceService.Get() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/helpers.go b/helpers.go index f39d7b4..130d949 100644 --- a/helpers.go +++ b/helpers.go @@ -17,6 +17,7 @@ func parseRadarrResponse(response *http.Response) error { if err != nil { return err } + if body["error"] != "" { e.Message = body["error"] } else if body["message"] != "" { diff --git a/internal/radarr/dummy.go b/internal/radarr/dummy.go index 17114b9..6f120f1 100644 --- a/internal/radarr/dummy.go +++ b/internal/radarr/dummy.go @@ -10,7 +10,8 @@ import ( "net/url" ) -var dummyMovieResponse string = ` +// DummyMovieResponse describe a dymmy movie +var DummyMovieResponse string = ` { "title": "Frozen II", "alternativeTitles": [ @@ -153,8 +154,23 @@ var dummySystemStatusResponse string = ` "runtimeName": "netCore" }` -var dummyMoviesResponse string = fmt.Sprintf("[%s, %s]", dummyMovieResponse, dummyMovieResponse) -var dummyUpcomingWithBothFilterResponse = fmt.Sprintf("[%s]", dummyMovieResponse) +var dummyDiskspaceResponse string = ` +[{ + "path": "/", + "label": "/", + "freeSpace": 11059175424, + "totalSpace": 15614754816 +}, +{ + "path": "/home", + "label": "/home", + "freeSpace": 88775757824, + "totalSpace": 98325770240 +} +]` + +var dummyMoviesResponse string = fmt.Sprintf("[%s, %s]", DummyMovieResponse, DummyMovieResponse) +var dummyUpcomingWithBothFilterResponse = fmt.Sprintf("[%s]", DummyMovieResponse) // DummyUnauthorizedResponse describe Unauthorized Radarr response var DummyUnauthorizedResponse string = `{"error": "Unauthorized"}` @@ -218,13 +234,6 @@ func (*mockedTransport2) RoundTrip(req *http.Request) (*http.Response, error) { return nil, errors.New("foo") } -// TestCase describe a test case -type TestCase struct { - Title string - Expected interface{} - Got interface{} -} - // HTTPClient implements HTTPClientInterface type HTTPClient struct{} @@ -254,7 +263,7 @@ func (c *HTTPClient) Get(targetURL string) (resp *http.Response, err error) { return &http.Response{ StatusCode: http.StatusOK, Status: http.StatusText(http.StatusOK), - Body: ioutil.NopCloser(bytes.NewBufferString(dummyMovieResponse)), + Body: ioutil.NopCloser(bytes.NewBufferString(DummyMovieResponse)), }, nil case fmt.Sprintf("%s/api%s?apikey=%s", DummyURL, "/movie", DummyAPIKey): @@ -281,6 +290,14 @@ func (c *HTTPClient) Get(targetURL string) (resp *http.Response, err error) { Body: ioutil.NopCloser(bytes.NewBufferString(dummyMoviesResponse)), }, nil + case fmt.Sprintf("%s/api%s?apikey=%s&end=%s", DummyURL, "/calendar", DummyAPIKey, dummyEndDate): + // Upcoming movies with end filter. Returns 0 movies + return &http.Response{ + StatusCode: http.StatusOK, + Status: http.StatusText(http.StatusOK), + Body: ioutil.NopCloser(bytes.NewBufferString(dummyEmptyListResponse)), + }, nil + case fmt.Sprintf("%s/api%s?apikey=%s&start=%s&end=%s", DummyURL, "/calendar", DummyAPIKey, dummyStartDate, dummyEndDate): // Upcoming movies with start filter and end filter. Return 1 movies return &http.Response{ @@ -304,6 +321,13 @@ func (c *HTTPClient) Get(targetURL string) (resp *http.Response, err error) { Body: ioutil.NopCloser(bytes.NewBufferString(dummySystemStatusResponse)), }, nil + case fmt.Sprintf("%s/api%s?apikey=%s", DummyURL, "/diskspace", DummyAPIKey): + return &http.Response{ + StatusCode: http.StatusOK, + Status: http.StatusText(http.StatusOK), + Body: ioutil.NopCloser(bytes.NewBufferString(dummyDiskspaceResponse)), + }, nil + default: // Defaulting to 404 return &http.Response{ diff --git a/movie.go b/movie.go index 0f06672..de5c9a5 100644 --- a/movie.go +++ b/movie.go @@ -2,6 +2,7 @@ package radarr import ( "encoding/json" + "errors" "fmt" "net/url" "time" @@ -196,7 +197,7 @@ func (m *MovieService) Upcoming(opts ...*UpcomingOptions) (*Movies, error) { // If both dates are filled, verify order if opts[0].Start != nil && opts[0].End != nil { if opts[0].End.Before(*opts[0].Start) || opts[0].Start.After(*opts[0].End) { - return nil, fmt.Errorf("Incorrect dates. Please ensure date are set properly") + return nil, errors.New("Incorrect dates. Please ensure date are set properly") } }