From bf013bda6deda4036682c6e365a7a757fd0fb1ff Mon Sep 17 00:00:00 2001 From: Joshua Raphael Date: Fri, 10 Jan 2025 14:30:28 -0700 Subject: [PATCH] issue-7 -- Add GetUsersFollowingMe endpoint --- README.md | 3 +- .../getusersfollowingme.go | 31 ++++ models/user.go | 21 +++ user.go | 25 ++++ user_test.go | 132 ++++++++++++++++++ 5 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 examples/user/getusersfollowingme/getusersfollowingme.go diff --git a/README.md b/README.md index d7fe708..bf04ce7 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ![coverage](https://raw.githubusercontent.com/joshraphael/go-retroachievements/badges/.badges/main/coverage.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/joshraphael/go-retroachievements)](https://goreportcard.com/report/github.com/joshraphael/go-retroachievements) [![GitHub Tag](https://img.shields.io/github/v/tag/joshraphael/go-retroachievements)](https://github.com/joshraphael/go-retroachievements/tags) -[![GitHub repo size](https://img.shields.io/github/repo-size/joshraphael/go-retroachievements)](https://github.com/joshraphael/go-retroachievements/archive/master.zip) +[![GitHub repo size](https://img.shields.io/github/repo-size/joshraphael/go-retroachievements)](https://github.com/joshraphael/go-retroachievements/archive/main.zip) ## Installation Use go get to install the latest version of the library. @@ -71,6 +71,7 @@ For convenience, the API docs and examples can be found in the tables below |`GetUserCompletedGames()`|[Deprecated] Get hardcore and softcore completion metadata about games a user has played.|[docs](https://api-docs.retroachievements.org/v1/get-user-completed-games.html) \| [example](examples/user/getusercompletedgames/getusercompletedgames.go)| |`GetUserWantToPlayList()`|Get a user's "Want to Play Games" list.|[docs](https://api-docs.retroachievements.org/v1/get-user-want-to-play-list.html) \| [example](examples/user/getuserwanttoplaylist/getuserwanttoplaylist.go)| |`GetUsersIFollow()`|Get the caller's "Following" users list.|[docs](https://api-docs.retroachievements.org/v1/get-users-i-follow.html) \| [example](examples/user/getusersifollow/getusersifollow.go)| +|`GetUsersFollowingMe()`|Get the caller's "Followers" users list.|[docs](https://api-docs.retroachievements.org/v1/get-users-following-me.html) \| [example](examples/user/getusersfollowingme/getusersfollowingme.go)| |`GetUserSetRequests()`|Get a user's list of set requests.|[docs](https://api-docs.retroachievements.org/v1/get-user-set-requests.html) \| [example](examples/user/getusersetrequests/getusersetrequests.go)|

Game

diff --git a/examples/user/getusersfollowingme/getusersfollowingme.go b/examples/user/getusersfollowingme/getusersfollowingme.go new file mode 100644 index 0000000..125db7c --- /dev/null +++ b/examples/user/getusersfollowingme/getusersfollowingme.go @@ -0,0 +1,31 @@ +// Package getusersfollowingme provides an example for the caller's "Followers" users list. +package main + +import ( + "fmt" + "os" + + "github.com/joshraphael/go-retroachievements" + "github.com/joshraphael/go-retroachievements/models" +) + +/* +Test script, add RA_API_KEY to your env and use `go run getusersfollowingme.go` +*/ +func main() { + secret := os.Getenv("RA_API_KEY") + + client := retroachievements.NewClient(secret) + + count := 1 + offset := 1 + resp, err := client.GetUsersFollowingMe(models.GetUsersFollowingMeParameters{ + Count: &count, + Offset: &offset, + }) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", resp) +} diff --git a/models/user.go b/models/user.go index 3116622..2b1b25e 100644 --- a/models/user.go +++ b/models/user.go @@ -626,6 +626,27 @@ type GetUsersIFollowResult struct { IsFollowingMe bool `json:"IsFollowingMe"` } +type GetUsersFollowingMeParameters struct { + // [Optional] The number of records to return (default: 100, max: 500). + Count *int + + // [Optional] The number of entries to skip (default: 0). + Offset *int +} + +type GetUsersFollowingMe struct { + Count int `json:"Count"` + Total int `json:"Total"` + Results []GetUsersFollowingMeResult `json:"Results"` +} + +type GetUsersFollowingMeResult struct { + User string `json:"User"` + Points int `json:"Points"` + PointsSoftcore int `json:"PointsSoftcore"` + AmIFollowing bool `json:"AmIFollowing"` +} + type GetUserSetRequestsParameters struct { // The target username. Username string diff --git a/user.go b/user.go index 5336dac..ae733c9 100644 --- a/user.go +++ b/user.go @@ -363,6 +363,31 @@ func (c *Client) GetUsersIFollow(params models.GetUsersIFollowParameters) (*mode return resp, nil } +// GetUsersIFollow gets the caller's "Followers" users list. +func (c *Client) GetUsersFollowingMe(params models.GetUsersFollowingMeParameters) (*models.GetUsersFollowingMe, error) { + details := []raHttp.RequestDetail{ + raHttp.Method(http.MethodGet), + raHttp.UserAgent(c.UserAgent), + raHttp.Path("/API/API_GetUsersFollowingMe.php"), + raHttp.APIToken(c.Secret), + } + if params.Count != nil { + details = append(details, raHttp.C(*params.Count)) + } + if params.Offset != nil { + details = append(details, raHttp.O(*params.Offset)) + } + r, err := c.do(details...) + if err != nil { + return nil, fmt.Errorf("calling endpoint: %w", err) + } + resp, err := raHttp.ResponseObject[models.GetUsersFollowingMe](r) + if err != nil { + return nil, fmt.Errorf("parsing response object: %w", err) + } + return resp, nil +} + // GetUserSetRequests gets a user's list of set requests. func (c *Client) GetUserSetRequests(params models.GetUserSetRequestsParameters) (*models.GetUserSetRequests, error) { details := []raHttp.RequestDetail{ diff --git a/user_test.go b/user_test.go index 6615295..9b2f3e8 100644 --- a/user_test.go +++ b/user_test.go @@ -2610,6 +2610,138 @@ func TestGetUsersIFollow(tt *testing.T) { } } +func TestGetUsersFollowingMe(tt *testing.T) { + count := 10 + offset := 23 + tests := []struct { + name string + params models.GetUsersFollowingMeParameters + modifyURL func(url string) string + responseCode int + responseMessage models.GetUsersFollowingMe + responseError models.ErrorResponse + response func(messageBytes []byte, errorBytes []byte) []byte + assert func(t *testing.T, resp *models.GetUsersFollowingMe, err error) + }{ + { + name: "fail to call endpoint", + params: models.GetUsersFollowingMeParameters{ + Count: &count, + Offset: &offset, + }, + modifyURL: func(url string) string { + return "" + }, + responseCode: http.StatusUnauthorized, + responseError: models.ErrorResponse{ + Message: "test", + Errors: []models.ErrorDetail{ + { + Status: http.StatusUnauthorized, + Code: "unauthorized", + Title: "Not Authorized", + }, + }, + }, + response: func(messageBytes []byte, errorBytes []byte) []byte { + return errorBytes + }, + assert: func(t *testing.T, resp *models.GetUsersFollowingMe, err error) { + require.Nil(t, resp) + require.EqualError(t, err, "calling endpoint: Get \"/API/API_GetUsersFollowingMe.php?c=10&o=23&y=some_secret\": unsupported protocol scheme \"\"") + }, + }, + { + name: "error response", + params: models.GetUsersFollowingMeParameters{ + Count: &count, + Offset: &offset, + }, + modifyURL: func(url string) string { + return url + }, + responseCode: http.StatusUnauthorized, + responseError: models.ErrorResponse{ + Message: "test", + Errors: []models.ErrorDetail{ + { + Status: http.StatusUnauthorized, + Code: "unauthorized", + Title: "Not Authorized", + }, + }, + }, + response: func(messageBytes []byte, errorBytes []byte) []byte { + return errorBytes + }, + assert: func(t *testing.T, resp *models.GetUsersFollowingMe, err error) { + require.Nil(t, resp) + require.EqualError(t, err, "parsing response object: error code 401 returned: {\"message\":\"test\",\"errors\":[{\"status\":401,\"code\":\"unauthorized\",\"title\":\"Not Authorized\"}]}") + }, + }, + { + name: "success", + params: models.GetUsersFollowingMeParameters{ + Count: &count, + Offset: &offset, + }, + modifyURL: func(url string) string { + return url + }, + responseCode: http.StatusOK, + responseMessage: models.GetUsersFollowingMe{ + Count: 20, + Total: 120, + Results: []models.GetUsersFollowingMeResult{ + { + User: "zuliman92", + Points: 1882, + PointsSoftcore: 258, + AmIFollowing: true, + }, + }, + }, + response: func(messageBytes []byte, errorBytes []byte) []byte { + return messageBytes + }, + assert: func(t *testing.T, resp *models.GetUsersFollowingMe, err error) { + require.NotNil(t, resp) + require.Equal(t, 20, resp.Count) + require.Equal(t, 120, resp.Total) + require.Len(t, resp.Results, 1) + require.Equal(t, "zuliman92", resp.Results[0].User) + require.Equal(t, 1882, resp.Results[0].Points) + require.Equal(t, 258, resp.Results[0].PointsSoftcore) + require.True(t, resp.Results[0].AmIFollowing) + require.NoError(t, err) + }, + }, + } + for _, test := range tests { + tt.Run(test.name, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + expectedPath := "/API/API_GetUsersFollowingMe.php" + if r.URL.Path != expectedPath { + t.Errorf("Expected to request '%s', got: %s", expectedPath, r.URL.Path) + } + w.WriteHeader(test.responseCode) + responseMessage, err := json.Marshal(test.responseMessage) + require.NoError(t, err) + errBytes, err := json.Marshal(test.responseError) + require.NoError(t, err) + resp := test.response(responseMessage, errBytes) + num, err := w.Write(resp) + require.NoError(t, err) + require.Equal(t, num, len(resp)) + })) + defer server.Close() + client := retroachievements.New(test.modifyURL(server.URL), "go-retroachievements/v0.0.0", "some_secret") + resp, err := client.GetUsersFollowingMe(test.params) + test.assert(t, resp, err) + }) + } +} + func TestGetUserSetRequests(tt *testing.T) { all := true tests := []struct {