feat(simkl): add comprehensive error handling
SimklFunctions.go: - Update SimklHelper to return (json.RawMessage, error) - Add status code validation (200-299 range) - Update SimklGetUserWatchlist to return (SimklWatchListType, error) - Update SimklSyncEpisodes to return (SimklAnime, error) - Update SimklSyncRating to return (SimklAnime, error) - Update SimklSyncStatus to return (SimklAnime, error) - Update SimklSearch to return (SimklAnime, error) - Update SimklSyncRemove to return (bool, error) - Add proper error wrapping for all failure scenarios SimklUserFunctions.go: - Replace log.Fatalf with log.Printf in server error handling (line 119) - Replace log.Fatal with log.Printf in JSON marshaling (line 141) All Simkl API calls now properly propagate errors to frontend.
This commit is contained in:
@@ -14,16 +14,24 @@ import (
|
|||||||
|
|
||||||
var SimklWatchList SimklWatchListType
|
var SimklWatchList SimklWatchListType
|
||||||
|
|
||||||
func SimklHelper(method string, url string, body interface{}) json.RawMessage {
|
func SimklHelper(method string, url string, body interface{}) (json.RawMessage, error) {
|
||||||
reader, _ := json.Marshal(body)
|
reader, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var req *http.Request
|
var req *http.Request
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
if body != nil {
|
if body != nil {
|
||||||
req, _ = http.NewRequest(method, url, bytes.NewBuffer(reader))
|
req, err = http.NewRequest(method, url, bytes.NewBuffer(reader))
|
||||||
} else {
|
} else {
|
||||||
req, _ = http.NewRequest(method, url, nil)
|
req, err = http.NewRequest(method, url, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
@@ -32,41 +40,45 @@ func SimklHelper(method string, url string, body interface{}) json.RawMessage {
|
|||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Errored when sending request to the server")
|
return nil, fmt.Errorf("network error: %w", err)
|
||||||
message, _ := json.Marshal(struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}{
|
|
||||||
Message: "Errored when sending request to the server" + err.Error(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
return respBody
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
return respBody, fmt.Errorf("API returned status: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return respBody, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
func (a *App) SimklGetUserWatchlist() (SimklWatchListType, error) {
|
||||||
method := "GET"
|
method := "GET"
|
||||||
url := "https://api.simkl.com/sync/all-items/anime"
|
url := "https://api.simkl.com/sync/all-items/anime"
|
||||||
|
|
||||||
respBody := SimklHelper(method, url, nil)
|
respBody, err := SimklHelper(method, url, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return SimklWatchListType{}, fmt.Errorf("failed to get Simkl watchlist: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var errCheck struct {
|
var errCheck struct {
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &errCheck)
|
err = json.Unmarshal(respBody, &errCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return SimklWatchListType{}, fmt.Errorf("failed to parse error response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errCheck.Error != "" {
|
if errCheck.Error != "" {
|
||||||
a.LogoutSimkl()
|
a.LogoutSimkl()
|
||||||
return SimklWatchListType{}
|
return SimklWatchListType{}, fmt.Errorf("Simkl API error: %s", errCheck.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
var watchlist SimklWatchListType
|
var watchlist SimklWatchListType
|
||||||
@@ -74,14 +86,15 @@ func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
|||||||
err = json.Unmarshal(respBody, &watchlist)
|
err = json.Unmarshal(respBody, &watchlist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return SimklWatchListType{}, fmt.Errorf("failed to parse watchlist: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
SimklWatchList = watchlist
|
SimklWatchList = watchlist
|
||||||
|
|
||||||
return watchlist
|
return watchlist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) (SimklAnime, error) {
|
||||||
var episodes []Episode
|
var episodes []Episode
|
||||||
var url string
|
var url string
|
||||||
var shows []SimklPostShow
|
var shows []SimklPostShow
|
||||||
@@ -112,13 +125,19 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
|||||||
|
|
||||||
simklSync := SimklSyncHistoryType{shows}
|
simklSync := SimklSyncHistoryType{shows}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, simklSync)
|
respBody, err := SimklHelper("POST", url, simklSync)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync episodes: %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to sync episodes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var success interface{}
|
var success interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &success)
|
err = json.Unmarshal(respBody, &success)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
@@ -131,10 +150,10 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
|||||||
|
|
||||||
WatchListUpdate(anime)
|
WatchListUpdate(anime)
|
||||||
|
|
||||||
return anime
|
return anime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
func (a *App) SimklSyncRating(anime SimklAnime, rating int) (SimklAnime, error) {
|
||||||
var url string
|
var url string
|
||||||
showWithRating := ShowWithRating{
|
showWithRating := ShowWithRating{
|
||||||
Title: anime.Show.Title,
|
Title: anime.Show.Title,
|
||||||
@@ -169,13 +188,17 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
|||||||
Shows []interface{} `json:"shows" ts_type:"shows"`
|
Shows []interface{} `json:"shows" ts_type:"shows"`
|
||||||
}{shows}
|
}{shows}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, simklSync)
|
respBody, err := SimklHelper("POST", url, simklSync)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync rating: %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to sync rating: %w", err)
|
||||||
|
}
|
||||||
var success interface{}
|
var success interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &success)
|
err = json.Unmarshal(respBody, &success)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
@@ -188,10 +211,10 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
|||||||
|
|
||||||
WatchListUpdate(anime)
|
WatchListUpdate(anime)
|
||||||
|
|
||||||
return anime
|
return anime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
func (a *App) SimklSyncStatus(anime SimklAnime, status string) (SimklAnime, error) {
|
||||||
url := "https://api.simkl.com/sync/add-to-list"
|
url := "https://api.simkl.com/sync/add-to-list"
|
||||||
show := SimklShowStatus{
|
show := SimklShowStatus{
|
||||||
Title: anime.Show.Title,
|
Title: anime.Show.Title,
|
||||||
@@ -211,13 +234,17 @@ func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
|||||||
Shows []SimklShowStatus `json:"shows" ts_type:"shows"`
|
Shows []SimklShowStatus `json:"shows" ts_type:"shows"`
|
||||||
}{shows}
|
}{shows}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, simklSync)
|
respBody, err := SimklHelper("POST", url, simklSync)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync status: %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to sync status: %w", err)
|
||||||
|
}
|
||||||
var success interface{}
|
var success interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &success)
|
err = json.Unmarshal(respBody, &success)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
@@ -230,15 +257,20 @@ func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
|||||||
|
|
||||||
WatchListUpdate(anime)
|
WatchListUpdate(anime)
|
||||||
|
|
||||||
return anime
|
return anime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
func (a *App) SimklSearch(aniListAnime MediaList) (SimklAnime, error) {
|
||||||
var result SimklAnime
|
var result SimklAnime
|
||||||
|
|
||||||
if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) {
|
if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) {
|
||||||
fmt.Println("Watchlist empty. Calling...")
|
fmt.Println("Watchlist empty. Calling...")
|
||||||
SimklWatchList = a.SimklGetUserWatchlist()
|
watchlist, err := a.SimklGetUserWatchlist()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to get watchlist: %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to load watchlist for search: %w", err)
|
||||||
|
}
|
||||||
|
SimklWatchList = watchlist
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, anime := range SimklWatchList.Anime {
|
for _, anime := range SimklWatchList.Anime {
|
||||||
@@ -255,22 +287,30 @@ func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
|||||||
var anime SimklSearchType
|
var anime SimklSearchType
|
||||||
url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID)
|
url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID)
|
||||||
|
|
||||||
respBody := SimklHelper("GET", url, nil)
|
respBody, err := SimklHelper("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
err := json.Unmarshal(respBody, &anime)
|
log.Printf("Failed to search Simkl: %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to search Simkl by AniList ID: %w", err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(respBody, &anime)
|
||||||
|
|
||||||
if len(anime) == 0 {
|
if len(anime) == 0 {
|
||||||
url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
|
url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
|
||||||
respBody = SimklHelper("GET", url, nil)
|
respBody, err = SimklHelper("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to search Simkl by MAL ID: %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to search by MAL ID: %w", err)
|
||||||
|
}
|
||||||
err = json.Unmarshal(respBody, &anime)
|
err = json.Unmarshal(respBody, &anime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to parse search results: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(anime) == 0 {
|
if len(anime) == 0 {
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, watchListAnime := range SimklWatchList.Anime {
|
for _, watchListAnime := range SimklWatchList.Anime {
|
||||||
@@ -288,10 +328,10 @@ func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncRemove(anime SimklAnime) bool {
|
func (a *App) SimklSyncRemove(anime SimklAnime) (bool, error) {
|
||||||
url := "https://api.simkl.com/sync/history/remove"
|
url := "https://api.simkl.com/sync/history/remove"
|
||||||
var showArray []SimklShowStatus
|
var showArray []SimklShowStatus
|
||||||
|
|
||||||
@@ -312,25 +352,28 @@ func (a *App) SimklSyncRemove(anime SimklAnime) bool {
|
|||||||
Shows: showArray,
|
Shows: showArray,
|
||||||
}
|
}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, show)
|
respBody, err := SimklHelper("POST", url, show)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync remove: %s\n", err)
|
||||||
|
return false, fmt.Errorf("failed to sync remove: %w", err)
|
||||||
|
}
|
||||||
var success SimklDeleteType
|
var success SimklDeleteType
|
||||||
|
err = json.Unmarshal(respBody, &success)
|
||||||
err := json.Unmarshal(respBody, &success)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return false, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if success.Deleted.Shows >= 1 {
|
if success.Deleted.Shows >= 1 {
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
|
if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
|
||||||
SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1)
|
SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true, nil
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false, fmt.Errorf("no shows were deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
func WatchListUpdate(anime SimklAnime) {
|
func WatchListUpdate(anime SimklAnime) {
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func (a *App) handleSimklCallback(wg *sync.WaitGroup) {
|
|||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Fatalf("listen: %s\n", err)
|
log.Printf("Server error: %s\n", err)
|
||||||
}
|
}
|
||||||
fmt.Println("Shutting down...")
|
fmt.Println("Shutting down...")
|
||||||
}()
|
}()
|
||||||
@@ -138,7 +138,8 @@ func getSimklAuthorizationToken(content string) SimklJWT {
|
|||||||
}
|
}
|
||||||
jsonData, err := json.Marshal(data)
|
jsonData, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Printf("Failed to marshal data: %s\n", err)
|
||||||
|
return SimklJWT{}
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := http.NewRequest("POST", "https://api.simkl.com/oauth/token", bytes.NewBuffer(jsonData))
|
response, err := http.NewRequest("POST", "https://api.simkl.com/oauth/token", bytes.NewBuffer(jsonData))
|
||||||
|
|||||||
Reference in New Issue
Block a user