diff --git a/README.md b/README.md index 38d0ca9..d7fe648 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ For convenience, the API docs and examples can be found in the tables below |`GetUserProgress()`|Get a user's progress on a list of specified games.|[docs](https://api-docs.retroachievements.org/v1/get-user-progress.html) \| [example](examples/user/getuserprogress/getuserprogress.go)| |`GetUserRecentlyPlayedGames()`|Get a list of games a user has recently played.|[docs](https://api-docs.retroachievements.org/v1/get-user-recently-played-games.html) \| [example](examples/user/getuserrecentlyplayedgames/getuserrecentlyplayedgames.go)| |`GetUserSummary()`|Get a user's profile metadata.|[docs](https://api-docs.retroachievements.org/v1/get-user-summary.html) \| [example](examples/user/getusersummary/getusersummary.go)| +|`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)|

Game

diff --git a/examples/user/getusercompletedgames/getusercompletedgames.go b/examples/user/getusercompletedgames/getusercompletedgames.go new file mode 100644 index 0000000..8d356f9 --- /dev/null +++ b/examples/user/getusercompletedgames/getusercompletedgames.go @@ -0,0 +1,28 @@ +// Package getusercompletedgames provides an example for getting a users completed games +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 getusercompletedgames.go` +*/ +func main() { + secret := os.Getenv("RA_API_KEY") + + client := retroachievements.NewClient(secret) + + resp, err := client.GetUserCompletedGames(models.GetUserCompletedGamesParameters{ + Username: "jamiras", + }) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", resp) +} diff --git a/examples/user/getuserprogress/getuserprogress.go b/examples/user/getuserprogress/getuserprogress.go index 2bc76df..f520de1 100644 --- a/examples/user/getuserprogress/getuserprogress.go +++ b/examples/user/getuserprogress/getuserprogress.go @@ -19,7 +19,6 @@ func main() { resp, err := client.GetUserProgress(models.GetUserProgressParameters{ Username: "jamiras", - GameIDs: []int{1, 16247}, }) if err != nil { panic(err) diff --git a/models/user.go b/models/user.go index 9249e21..294ee0a 100644 --- a/models/user.go +++ b/models/user.go @@ -562,3 +562,20 @@ type GetUserSummaryLastGame struct { Released LongMonthDate `json:"Released"` IsFinal int `json:"IsFinal"` } + +type GetUserCompletedGamesParameters struct { + // The target username + Username string +} + +type GetUserCompletedGames struct { + GameID int `json:"GameID"` + Title string `json:"Title"` + ImageIcon string `json:"ImageIcon"` + ConsoleID int `json:"ConsoleID"` + ConsoleName string `json:"ConsoleName"` + MaxPossible int `json:"MaxPossible"` + NumAwarded int `json:"NumAwarded"` + PctWon string `json:"PctWon"` + HardcoreMode string `json:"HardcoreMode"` +} diff --git a/user.go b/user.go index a5de24e..d5efb12 100644 --- a/user.go +++ b/user.go @@ -202,7 +202,7 @@ func (c *Client) GetUserPoints(params models.GetUserPointsParameters) (*models.G } // GetUserProgress get a user's progress on a list of specified games. -func (c *Client) GetUserProgress(params models.GetUserProgressParameters) (map[string]models.GetUserProgress, error) { +func (c *Client) GetUserProgress(params models.GetUserProgressParameters) (*map[string]models.GetUserProgress, error) { resp, err := c.do( raHttp.Method(http.MethodGet), raHttp.Path("/API/API_GetUserProgress.php"), @@ -217,7 +217,7 @@ func (c *Client) GetUserProgress(params models.GetUserProgressParameters) (map[s if err != nil { return nil, fmt.Errorf("parsing response object: %w", err) } - return *progress, nil + return progress, nil } // GetUserRecentlyPlayedGames get a list of games a user has recently played. @@ -269,3 +269,21 @@ func (c *Client) GetUserSummary(params models.GetUserSummaryParameters) (*models } return recentlyPlayed, nil } + +// GetUserCompletedGames gets completion metadata about the games a given user has played. +func (c *Client) GetUserCompletedGames(params models.GetUserCompletedGamesParameters) ([]models.GetUserCompletedGames, error) { + resp, err := c.do( + raHttp.Method(http.MethodGet), + raHttp.Path("/API/API_GetUserCompletedGames.php"), + raHttp.APIToken(c.Secret), + raHttp.Username(params.Username), + ) + if err != nil { + return nil, fmt.Errorf("calling endpoint: %w", err) + } + games, err := raHttp.ResponseList[models.GetUserCompletedGames](resp) + if err != nil { + return nil, fmt.Errorf("parsing response list: %w", err) + } + return games, nil +} diff --git a/user_test.go b/user_test.go index 77f095a..866332a 100644 --- a/user_test.go +++ b/user_test.go @@ -1582,7 +1582,7 @@ func TestGetUserProgress(tt *testing.T) { responseMessage map[string]models.GetUserProgress responseError models.ErrorResponse response func(messageBytes []byte, errorBytes []byte) []byte - assert func(t *testing.T, progress map[string]models.GetUserProgress, err error) + assert func(t *testing.T, progress *map[string]models.GetUserProgress, err error) }{ { name: "fail to call endpoint", @@ -1607,7 +1607,7 @@ func TestGetUserProgress(tt *testing.T) { response: func(messageBytes []byte, errorBytes []byte) []byte { return errorBytes }, - assert: func(t *testing.T, resp map[string]models.GetUserProgress, err error) { + assert: func(t *testing.T, resp *map[string]models.GetUserProgress, err error) { require.Nil(t, resp) require.EqualError(t, err, "calling endpoint: Get \"/API/API_GetUserProgress.php?i=1%2C2%2C5352&u=Test&y=some_secret\": unsupported protocol scheme \"\"") }, @@ -1635,7 +1635,7 @@ func TestGetUserProgress(tt *testing.T) { response: func(messageBytes []byte, errorBytes []byte) []byte { return errorBytes }, - assert: func(t *testing.T, resp map[string]models.GetUserProgress, err error) { + assert: func(t *testing.T, resp *map[string]models.GetUserProgress, err error) { require.Nil(t, resp) require.EqualError(t, err, "parsing response object: error responses: [401] Not Authorized") }, @@ -1679,10 +1679,11 @@ func TestGetUserProgress(tt *testing.T) { response: func(messageBytes []byte, errorBytes []byte) []byte { return messageBytes }, - assert: func(t *testing.T, resp map[string]models.GetUserProgress, err error) { + assert: func(t *testing.T, resp *map[string]models.GetUserProgress, err error) { require.NotNil(t, resp) + r := *resp // first element - first, ok := resp["1"] + first, ok := r["1"] require.True(t, ok) require.Equal(t, 36, first.NumPossibleAchievements) require.Equal(t, 305, first.PossibleScore) @@ -1692,7 +1693,7 @@ func TestGetUserProgress(tt *testing.T) { require.Equal(t, 100, first.ScoreAchievedHardcore) // second element - second, ok := resp["2"] + second, ok := r["2"] require.True(t, ok) require.Equal(t, 56, second.NumPossibleAchievements) require.Equal(t, 600, second.PossibleScore) @@ -1702,7 +1703,7 @@ func TestGetUserProgress(tt *testing.T) { require.Equal(t, 0, second.ScoreAchievedHardcore) // third element - third, ok := resp["5352"] + third, ok := r["5352"] require.True(t, ok) require.Equal(t, 13, third.NumPossibleAchievements) require.Equal(t, 230, third.PossibleScore) @@ -2220,3 +2221,133 @@ func TestGetUserSummary(tt *testing.T) { }) } } + +func TestGetUserCompletedGames(tt *testing.T) { + tests := []struct { + name string + params models.GetUserCompletedGamesParameters + modifyURL func(url string) string + responseCode int + responseMessage []models.GetUserCompletedGames + responseError models.ErrorResponse + response func(messageBytes []byte, errorBytes []byte) []byte + assert func(t *testing.T, resp []models.GetUserCompletedGames, err error) + }{ + { + name: "fail to call endpoint", + params: models.GetUserCompletedGamesParameters{ + Username: "Test", + }, + 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.GetUserCompletedGames, err error) { + require.Nil(t, resp) + require.EqualError(t, err, "calling endpoint: Get \"/API/API_GetUserCompletedGames.php?u=Test&y=some_secret\": unsupported protocol scheme \"\"") + }, + }, + { + name: "error response", + params: models.GetUserCompletedGamesParameters{ + Username: "Test", + }, + 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.GetUserCompletedGames, err error) { + require.Nil(t, resp) + require.EqualError(t, err, "parsing response list: error responses: [401] Not Authorized") + }, + }, + { + name: "success", + params: models.GetUserCompletedGamesParameters{ + Username: "Test", + }, + modifyURL: func(url string) string { + return url + }, + responseCode: http.StatusOK, + responseMessage: []models.GetUserCompletedGames{ + { + GameID: 24941, + Title: "Dragon Quest IV: Chapters of the Chosen [Subset - Plentiful Plunder]", + ImageIcon: "/Images/075762.png", + ConsoleID: 18, + ConsoleName: "Nintendo DS", + MaxPossible: 202, + NumAwarded: 202, + PctWon: "1.0000", + HardcoreMode: "1", + }, + }, + response: func(messageBytes []byte, errorBytes []byte) []byte { + return messageBytes + }, + assert: func(t *testing.T, resp []models.GetUserCompletedGames, err error) { + require.NoError(t, err) + require.Len(t, resp, 1) + require.Equal(t, 24941, resp[0].GameID) + require.Equal(t, "Dragon Quest IV: Chapters of the Chosen [Subset - Plentiful Plunder]", resp[0].Title) + require.Equal(t, "/Images/075762.png", resp[0].ImageIcon) + require.Equal(t, 18, resp[0].ConsoleID) + require.Equal(t, "Nintendo DS", resp[0].ConsoleName) + require.Equal(t, 202, resp[0].MaxPossible) + require.Equal(t, 202, resp[0].NumAwarded) + require.Equal(t, "1.0000", resp[0].PctWon) + require.Equal(t, "1", resp[0].HardcoreMode) + }, + }, + } + 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_GetUserCompletedGames.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), "some_secret") + resp, err := client.GetUserCompletedGames(test.params) + test.assert(t, resp, err) + }) + } +}