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)
+ })
+ }
+}