From fa23bf5e46698c31d9da2a899b4fd927c84fb281 Mon Sep 17 00:00:00 2001 From: Fuochi Date: Fri, 4 Mar 2022 10:40:59 +0400 Subject: [PATCH] feat(sonarr): add media management tab resources --- sonarr/mediamanagement.go | 68 ++++++++++ sonarr/mediamanagement_test.go | 154 ++++++++++++++++++++++ sonarr/naming.go | 65 ++++++++++ sonarr/naming_test.go | 145 +++++++++++++++++++++ sonarr/rootfolder.go | 89 +++++++++++++ sonarr/rootfolder_test.go | 226 +++++++++++++++++++++++++++++++++ sonarr/sonarr.go | 16 --- sonarr/type.go | 9 -- 8 files changed, 747 insertions(+), 25 deletions(-) create mode 100644 sonarr/mediamanagement.go create mode 100644 sonarr/mediamanagement_test.go create mode 100644 sonarr/naming.go create mode 100644 sonarr/naming_test.go create mode 100644 sonarr/rootfolder.go create mode 100644 sonarr/rootfolder_test.go diff --git a/sonarr/mediamanagement.go b/sonarr/mediamanagement.go new file mode 100644 index 0000000..3563715 --- /dev/null +++ b/sonarr/mediamanagement.go @@ -0,0 +1,68 @@ +package sonarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" +) + +type MediaManagement struct { + AutoUnmonitorPreviouslyDownloadedEpisodes bool `json:"autoUnmonitorPreviouslyDownloadedEpisodes,omitempty"` + CopyUsingHardlinks bool `json:"copyUsingHardlinks,omitempty"` + CreateEmptySeriesFolders bool `json:"createEmptySeriesFolders,omitempty"` + DeleteEmptyFolders bool `json:"deleteEmptyFolders,omitempty"` + EnableMediaInfo bool `json:"enableMediaInfo,omitempty"` + ImportExtraFiles bool `json:"importExtraFiles,omitempty"` + SetPermissionsLinux bool `json:"setPermissionsLinux,omitempty"` + SkipFreeSpaceCheckWhenImporting bool `json:"skipFreeSpaceCheckWhenImporting,omitempty"` + ID int64 `json:"id,omitempty"` + MinimumFreeSpaceWhenImporting int64 `json:"minimumFreeSpaceWhenImporting,omitempty"` + RecycleBinCleanupDays int64 `json:"recycleBinCleanupDays,omitempty"` + ChmodFolder string `json:"chmodFolder,omitempty"` + ChownGroup string `json:"chownGroup,omitempty"` + DownloadPropersAndRepacks string `json:"downloadPropersAndRepacks,omitempty"` + EpisodeTitleRequired string `json:"episodeTitleRequired,omitempty"` + ExtraFileExtensions string `json:"extraFileExtensions,omitempty"` + FileDate string `json:"fileDate,omitempty"` + RecycleBin string `json:"recycleBin,omitempty"` + RescanAfterRefresh string `json:"rescanAfterRefresh,omitempty"` +} + +// Define Base Path for MediaManagement calls. +const bpMediaManagement = APIver + "/config/mediaManagement" + +// GetMediaManagement returns the mediaManagement. +func (s *Sonarr) GetMediaManagement() (*MediaManagement, error) { + return s.GetMediaManagementContext(context.Background()) +} + +func (s *Sonarr) GetMediaManagementContext(ctx context.Context) (*MediaManagement, error) { + var output *MediaManagement + + if _, err := s.GetInto(ctx, bpMediaManagement, nil, &output); err != nil { + return nil, fmt.Errorf("api.Get(mediaManagement): %w", err) + } + + return output, nil +} + +// UpdateMediaManagement updates the mediaManagement. +func (s *Sonarr) UpdateMediaManagement(mMgt *MediaManagement) (*MediaManagement, error) { + return s.UpdateMediaManagementContext(context.Background(), mMgt) +} + +func (s *Sonarr) UpdateMediaManagementContext(ctx context.Context, mMgt *MediaManagement) (*MediaManagement, error) { + var output MediaManagement + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(mMgt); err != nil { + return nil, fmt.Errorf("json.Marshal(mediaManagement): %w", err) + } + + if _, err := s.PutInto(ctx, bpMediaManagement, nil, &body, &output); err != nil { + return nil, fmt.Errorf("api.Put(mediaManagement): %w", err) + } + + return &output, nil +} diff --git a/sonarr/mediamanagement_test.go b/sonarr/mediamanagement_test.go new file mode 100644 index 0000000..10dfb05 --- /dev/null +++ b/sonarr/mediamanagement_test.go @@ -0,0 +1,154 @@ +package sonarr_test + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "golift.io/starr" + "golift.io/starr/sonarr" +) + +const mediaManagementBody = `{ + "autoUnmonitorPreviouslyDownloadedEpisodes": false, + "recycleBin": "", + "recycleBinCleanupDays": 7, + "downloadPropersAndRepacks": "preferAndUpgrade", + "createEmptySeriesFolders": false, + "deleteEmptyFolders": false, + "fileDate": "none", + "rescanAfterRefresh": "always", + "setPermissionsLinux": false, + "chmodFolder": "755", + "chownGroup": "", + "episodeTitleRequired": "always", + "skipFreeSpaceCheckWhenImporting": false, + "minimumFreeSpaceWhenImporting": 100, + "copyUsingHardlinks": true, + "importExtraFiles": false, + "extraFileExtensions": "srt", + "enableMediaInfo": true, + "id": 1 + }` + +func TestGetMediaManagement(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: mediaManagementBody, + WithResponse: &sonarr.MediaManagement{ + ID: 1, + AutoUnmonitorPreviouslyDownloadedEpisodes: false, + RecycleBin: "", + RecycleBinCleanupDays: 7, + DownloadPropersAndRepacks: "preferAndUpgrade", + CreateEmptySeriesFolders: false, + DeleteEmptyFolders: false, + FileDate: "none", + RescanAfterRefresh: "always", + SetPermissionsLinux: false, + ChmodFolder: "755", + ChownGroup: "", + EpisodeTitleRequired: "always", + SkipFreeSpaceCheckWhenImporting: false, + MinimumFreeSpaceWhenImporting: 100, + CopyUsingHardlinks: true, + ImportExtraFiles: false, + ExtraFileExtensions: "srt", + EnableMediaInfo: true, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.MediaManagement)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetMediaManagement() + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestUpdateMediaManagement(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "202", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedMethod: "PUT", + ResponseStatus: 202, + WithRequest: &sonarr.MediaManagement{ + EnableMediaInfo: true, + }, + ExpectedRequest: `{"enableMediaInfo":true}` + "\n", + ResponseBody: mediaManagementBody, + WithResponse: &sonarr.MediaManagement{ + ID: 1, + AutoUnmonitorPreviouslyDownloadedEpisodes: false, + RecycleBin: "", + RecycleBinCleanupDays: 7, + DownloadPropersAndRepacks: "preferAndUpgrade", + CreateEmptySeriesFolders: false, + DeleteEmptyFolders: false, + FileDate: "none", + RescanAfterRefresh: "always", + SetPermissionsLinux: false, + ChmodFolder: "755", + ChownGroup: "", + EpisodeTitleRequired: "always", + SkipFreeSpaceCheckWhenImporting: false, + MinimumFreeSpaceWhenImporting: 100, + CopyUsingHardlinks: true, + ImportExtraFiles: false, + ExtraFileExtensions: "srt", + EnableMediaInfo: true, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/mediaManagement"), + ExpectedMethod: "PUT", + WithRequest: &sonarr.MediaManagement{ + EnableMediaInfo: true, + }, + ExpectedRequest: `{"enableMediaInfo":true}` + "\n", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.MediaManagement)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.UpdateMediaManagement(test.WithRequest.(*sonarr.MediaManagement)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} diff --git a/sonarr/naming.go b/sonarr/naming.go new file mode 100644 index 0000000..8523549 --- /dev/null +++ b/sonarr/naming.go @@ -0,0 +1,65 @@ +package sonarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" +) + +type Naming struct { + RenameEpisodes bool `json:"renameEpisodes,omitempty"` + ReplaceIllegalCharacters bool `json:"replaceIllegalCharacters,omitempty"` + IncludeQuality bool `json:"includeQuality,omitempty"` + IncludeSeriesTitle bool `json:"includeSeriesTitle,omitempty"` + IncludeEpisodeTitle bool `json:"includeEpisodeTitle,omitempty"` + ReplaceSpaces bool `json:"replaceSpaces,omitempty"` + ID int64 `json:"id,omitempty"` + MultiEpisodeStyle int64 `json:"multiEpisodeStyle,omitempty"` + Separator string `json:"separator,omitempty"` + NumberStyle string `json:"numberStyle,omitempty"` + DailyEpisodeFormat string `json:"dailyEpisodeFormat,omitempty"` + AnimeEpisodeFormat string `json:"animeEpisodeFormat,omitempty"` + SeriesFolderFormat string `json:"seriesFolderFormat,omitempty"` + SeasonFolderFormat string `json:"seasonFolderFormat,omitempty"` + SpecialsFolderFormat string `json:"specialsFolderFormat,omitempty"` + StandardEpisodeFormat string `json:"standardEpisodeFormat,omitempty"` +} + +// Define Base Path for Naming calls. +const bpNaming = APIver + "/config/naming" + +// GetNaming returns the naming. +func (s *Sonarr) GetNaming() (*Naming, error) { + return s.GetNamingContext(context.Background()) +} + +func (s *Sonarr) GetNamingContext(ctx context.Context) (*Naming, error) { + var output *Naming + + if _, err := s.GetInto(ctx, bpNaming, nil, &output); err != nil { + return nil, fmt.Errorf("api.Get(naming): %w", err) + } + + return output, nil +} + +// UpdateNaming updates the naming. +func (s *Sonarr) UpdateNaming(naming *Naming) (*Naming, error) { + return s.UpdateNamingContext(context.Background(), naming) +} + +func (s *Sonarr) UpdateNamingContext(ctx context.Context, naming *Naming) (*Naming, error) { + var output Naming + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(naming); err != nil { + return nil, fmt.Errorf("json.Marshal(naming): %w", err) + } + + if _, err := s.PutInto(ctx, bpNaming, nil, &body, &output); err != nil { + return nil, fmt.Errorf("api.Put(naming): %w", err) + } + + return &output, nil +} diff --git a/sonarr/naming_test.go b/sonarr/naming_test.go new file mode 100644 index 0000000..3a21e63 --- /dev/null +++ b/sonarr/naming_test.go @@ -0,0 +1,145 @@ +package sonarr_test + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "golift.io/starr" + "golift.io/starr/sonarr" +) + +const namingBody = `{ + "renameEpisodes": false, + "replaceIllegalCharacters": true, + "multiEpisodeStyle": 0, + "standardEpisodeFormat": "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", + "dailyEpisodeFormat": "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}", + "animeEpisodeFormat": "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", + "seriesFolderFormat": "{Series Title}", + "seasonFolderFormat": "Season {season}", + "specialsFolderFormat": "Specials", + "includeSeriesTitle": true, + "includeEpisodeTitle": false, + "includeQuality": false, + "replaceSpaces": true, + "separator": " - ", + "numberStyle": "S{season:00}E{episode:00}", + "id": 1 +}` + +func TestGetNaming(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: namingBody, + WithResponse: &sonarr.Naming{ + ID: 1, + RenameEpisodes: false, + ReplaceIllegalCharacters: true, + MultiEpisodeStyle: 0, + StandardEpisodeFormat: "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", + DailyEpisodeFormat: "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}", + AnimeEpisodeFormat: "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", + SeriesFolderFormat: "{Series Title}", + SeasonFolderFormat: "Season {season}", + SpecialsFolderFormat: "Specials", + IncludeSeriesTitle: true, + IncludeEpisodeTitle: false, + IncludeQuality: false, + ReplaceSpaces: true, + Separator: " - ", + NumberStyle: "S{season:00}E{episode:00}", + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.Naming)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetNaming() + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestUpdateNaming(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "202", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedMethod: "PUT", + ResponseStatus: 202, + WithRequest: &sonarr.Naming{ + ReplaceIllegalCharacters: true, + }, + ExpectedRequest: `{"replaceIllegalCharacters":true}` + "\n", + ResponseBody: namingBody, + WithResponse: &sonarr.Naming{ + ID: 1, + RenameEpisodes: false, + ReplaceIllegalCharacters: true, + MultiEpisodeStyle: 0, + StandardEpisodeFormat: "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", + DailyEpisodeFormat: "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}", + AnimeEpisodeFormat: "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}", + SeriesFolderFormat: "{Series Title}", + SeasonFolderFormat: "Season {season}", + SpecialsFolderFormat: "Specials", + IncludeSeriesTitle: true, + IncludeEpisodeTitle: false, + IncludeQuality: false, + ReplaceSpaces: true, + Separator: " - ", + NumberStyle: "S{season:00}E{episode:00}", + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "config/naming"), + ExpectedMethod: "PUT", + WithRequest: &sonarr.Naming{ + ReplaceIllegalCharacters: true, + }, + ExpectedRequest: `{"replaceIllegalCharacters":true}` + "\n", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.Naming)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.UpdateNaming(test.WithRequest.(*sonarr.Naming)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, output, test.WithResponse, "response is not the same as expected") + }) + } +} diff --git a/sonarr/rootfolder.go b/sonarr/rootfolder.go new file mode 100644 index 0000000..a813250 --- /dev/null +++ b/sonarr/rootfolder.go @@ -0,0 +1,89 @@ +package sonarr + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "path" + "strconv" + + "golift.io/starr" +) + +// RootFolder is the /api/v3/rootfolder endpoint. +type RootFolder struct { + Accessible bool `json:"accessible,omitempty"` + ID int64 `json:"id,omitempty"` + FreeSpace int64 `json:"freeSpace,omitempty"` + Path string `json:"path"` + UnmappedFolders []*starr.Path `json:"unmappedFolders,omitempty"` +} + +// Define Base Path for MediaManagement calls. +const bpRootFolder = APIver + "/rootFolder" + +// GetRootFolders returns all configured root folders. +func (s *Sonarr) GetRootFolders() ([]*RootFolder, error) { + return s.GetRootFoldersContext(context.Background()) +} + +func (s *Sonarr) GetRootFoldersContext(ctx context.Context) ([]*RootFolder, error) { + var output []*RootFolder + + if _, err := s.GetInto(ctx, bpRootFolder, nil, &output); err != nil { + return nil, fmt.Errorf("api.Get(rootFolder): %w", err) + } + + return output, nil +} + +// GetRootFolder returns a single root folder. +func (s *Sonarr) GetRootFolder(folderID int) (*RootFolder, error) { + return s.GetRootFolderContext(context.Background(), folderID) +} + +func (s *Sonarr) GetRootFolderContext(ctx context.Context, folderID int) (*RootFolder, error) { + var output *RootFolder + + uri := path.Join(bpRootFolder, strconv.Itoa(folderID)) + if _, err := s.GetInto(ctx, uri, nil, &output); err != nil { + return nil, fmt.Errorf("api.Get(rootFolder): %w", err) + } + + return output, nil +} + +// AddRootFolder creates a root folder. +func (s *Sonarr) AddRootFolder(folder *RootFolder) (*RootFolder, error) { + return s.AddRootFolderContext(context.Background(), folder) +} + +func (s *Sonarr) AddRootFolderContext(ctx context.Context, folder *RootFolder) (*RootFolder, error) { + var output RootFolder + + var body bytes.Buffer + if err := json.NewEncoder(&body).Encode(folder); err != nil { + return nil, fmt.Errorf("json.Marshal(rootFolder): %w", err) + } + + if _, err := s.PostInto(ctx, bpRootFolder, nil, &body, &output); err != nil { + return nil, fmt.Errorf("api.Post(rootFolder): %w", err) + } + + return &output, nil +} + +// DeleteRootFolder removes a single root folder. +func (s *Sonarr) DeleteRootFolder(folderID int) error { + return s.DeleteRootFolderContext(context.Background(), folderID) +} + +func (s *Sonarr) DeleteRootFolderContext(ctx context.Context, folderID int) error { + uri := path.Join(bpRootFolder, strconv.Itoa(folderID)) + if _, err := s.Delete(ctx, uri, nil); err != nil { + return fmt.Errorf("api.Delete(rootFolder): %w", err) + } + + return nil +} diff --git a/sonarr/rootfolder_test.go b/sonarr/rootfolder_test.go new file mode 100644 index 0000000..00304b9 --- /dev/null +++ b/sonarr/rootfolder_test.go @@ -0,0 +1,226 @@ +package sonarr_test + +import ( + "path" + "testing" + + "github.com/stretchr/testify/assert" + "golift.io/starr" + "golift.io/starr/sonarr" +) + +const ( + firstRootFolder = `{ + "path": "/series", + "accessible": true, + "freeSpace": 252221177856, + "unmappedFolders": [], + "id": 1 + }` + secondRootFolder = `{ + "path": "/miniseries", + "accessible": true, + "freeSpace": 252221177856, + "unmappedFolders": [ + { + "name": "1", + "path": "/miniseries/1" + } + ], + "id": 2 + }` +) + +func TestGetRootFolders(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder"), + ExpectedMethod: "GET", + ResponseStatus: 200, + ResponseBody: `[` + firstRootFolder + `,` + secondRootFolder + `]`, + WithResponse: []*sonarr.RootFolder{ + { + Path: "/series", + Accessible: true, + FreeSpace: 252221177856, + UnmappedFolders: []*starr.Path{}, + ID: 1, + }, + { + Path: "/miniseries", + Accessible: true, + FreeSpace: 252221177856, + UnmappedFolders: []*starr.Path{ + { + Name: "1", + Path: "/miniseries/1", + }, + }, + ID: 2, + }, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder"), + ExpectedMethod: "GET", + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: []*sonarr.RootFolder(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetRootFolders() + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestGetRootFolder(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/1"), + ExpectedMethod: "GET", + ResponseStatus: 200, + WithRequest: 1, + ResponseBody: firstRootFolder, + WithResponse: &sonarr.RootFolder{ + Path: "/series", + Accessible: true, + FreeSpace: 252221177856, + UnmappedFolders: []*starr.Path{}, + ID: 1, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/1"), + ExpectedMethod: "GET", + ResponseStatus: 404, + WithRequest: 1, + ResponseBody: `{"message": "NotFound"}`, + WithResponse: (*sonarr.RootFolder)(nil), + WithError: starr.ErrInvalidStatusCode, + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.GetRootFolder(test.WithRequest.(int)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestAddRootFolder(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "201", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder"), + ExpectedMethod: "POST", + ResponseStatus: 201, + WithRequest: &sonarr.RootFolder{ + Path: "/miniseries", + }, + ExpectedRequest: `{"path":"/miniseries"}` + "\n", + ResponseBody: secondRootFolder, + WithResponse: &sonarr.RootFolder{ + Path: "/miniseries", + Accessible: true, + FreeSpace: 252221177856, + UnmappedFolders: []*starr.Path{ + { + Name: "1", + Path: "/miniseries/1", + }, + }, + ID: 2, + }, + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder"), + ExpectedMethod: "POST", + ResponseStatus: 404, + WithRequest: &sonarr.RootFolder{ + Path: "/miniseries", + }, + ExpectedRequest: `{"path":"/miniseries"}` + "\n", + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + WithResponse: (*sonarr.RootFolder)(nil), + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + output, err := client.AddRootFolder(test.WithRequest.(*sonarr.RootFolder)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + assert.EqualValues(t, test.WithResponse, output, "response is not the same as expected") + }) + } +} + +func TestDeleteRootFolder(t *testing.T) { + t.Parallel() + + tests := []*starr.TestMockData{ + { + Name: "200", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/2"), + ExpectedMethod: "DELETE", + WithRequest: 2, + ResponseStatus: 200, + ResponseBody: "{}", + WithError: nil, + }, + { + Name: "404", + ExpectedPath: path.Join("/", starr.API, sonarr.APIver, "rootFolder/2"), + ExpectedMethod: "DELETE", + WithRequest: 2, + ResponseStatus: 404, + ResponseBody: `{"message": "NotFound"}`, + WithError: starr.ErrInvalidStatusCode, + }, + } + + for _, test := range tests { + test := test + t.Run(test.Name, func(t *testing.T) { + t.Parallel() + mockServer := test.GetMockServer(t) + client := sonarr.New(starr.New("mockAPIkey", mockServer.URL, 0)) + err := client.DeleteRootFolder(test.WithRequest.(int)) + assert.ErrorIs(t, err, test.WithError, "error is not the same as expected") + }) + } +} diff --git a/sonarr/sonarr.go b/sonarr/sonarr.go index 44e0984..9eef4c7 100644 --- a/sonarr/sonarr.go +++ b/sonarr/sonarr.go @@ -109,19 +109,3 @@ func (s *Sonarr) GetQueuePageContext(ctx context.Context, params *starr.Req) (*Q return &queue, nil } - -// GetRootFolders returns all configured root folders. -func (s *Sonarr) GetRootFolders() ([]*RootFolder, error) { - return s.GetRootFoldersContext(context.Background()) -} - -func (s *Sonarr) GetRootFoldersContext(ctx context.Context) ([]*RootFolder, error) { - var folders []*RootFolder - - _, err := s.GetInto(ctx, "v3/rootfolder", nil, &folders) - if err != nil { - return nil, fmt.Errorf("api.Get(rootfolder): %w", err) - } - - return folders, nil -} diff --git a/sonarr/type.go b/sonarr/type.go index c501a8a..4ff394a 100644 --- a/sonarr/type.go +++ b/sonarr/type.go @@ -39,15 +39,6 @@ type SystemStatus struct { PackageUpdateMechanism string `json:"packageUpdateMechanism"` } -// RootFolder is the /api/v3/rootfolder endpoint. -type RootFolder struct { - ID int64 `json:"id"` - Path string `json:"path"` - Accessible bool `json:"accessible"` - FreeSpace int64 `json:"freeSpace"` - UnmappedFolders []*starr.Path `json:"unmappedFolders"` -} - // Queue is the /api/v3/queue endpoint. type Queue struct { Page int `json:"page"`