Skip to content

Commit

Permalink
add GetUserAwards endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
joshraphael committed Aug 19, 2024
1 parent 2f5ac2d commit 856a97d
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ For convenience, the API docs and examples can be found in the tables below
|`GetAchievementsEarnedOnDay(string,Time)`|Get a list of achievements earned by a user on a given date.|[docs](https://api-docs.retroachievements.org/v1/get-achievements-earned-on-day.html) \| [example](examples/user/getachievementsearnedonday/getachievementsearnedonday.go)|
|`GetGameInfoAndUserProgress(string,int,bool)`|Get metadata about a game as well as a user's progress on that game.|[docs](https://api-docs.retroachievements.org/v1/get-game-info-and-user-progress.html) \| [example](examples/user/getgameinfoanduserprogress/getgameinfoanduserprogress.go)|
|`GetUserCompletionProgress(string)`|Get metadata about all the user's played games and any awards associated with them.|[docs](https://api-docs.retroachievements.org/v1/get-user-completion-progress.html) \| [example](examples/user/getusercompletionprogress/getusercompletionprogress.go)|
|`GetUserAwards(string)`|Get a list of a user's site awards/badges.|[docs](https://api-docs.retroachievements.org/v1/get-user-awards.html) \| [example](examples/user/getuserawards/getuserawards.go)|

<h3>Game</h3>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package getgameinfoanduserprogress provides an example for a users achievements in the last X minutes
// Package getgameinfoanduserprogress provides an example for a users game info progress
package main

import (
Expand All @@ -16,7 +16,7 @@ func main() {

client := retroachievements.NewClient(secret)

resp, err := client.GetGameInfoAndUserProgress("joshraphael", 515, true)
resp, err := client.GetGameInfoAndUserProgress("jamiras", 515, true)
if err != nil {
panic(err)
}
Expand Down
25 changes: 25 additions & 0 deletions examples/user/getuserawards/getuserawards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Package getuserawards provides an example for getting a users awards
package main

import (
"fmt"
"os"

"github.com/joshraphael/go-retroachievements"
)

/*
Test script, add RA_API_KEY to your env and use `go run getuserawards.go`
*/
func main() {
secret := os.Getenv("RA_API_KEY")

client := retroachievements.NewClient(secret)

resp, err := client.GetUserAwards("jamiras")
if err != nil {
panic(err)
}

fmt.Printf("%+v\n", resp)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package getusercompletionprogress provides an example for getting a users profile
// Package getusercompletionprogress provides an example for getting a users completion progress
package main

import (
Expand All @@ -16,7 +16,7 @@ func main() {

client := retroachievements.NewClient(secret)

resp, err := client.GetUserCompletionProgress("joshraphael")
resp, err := client.GetUserCompletionProgress("jamiras")
if err != nil {
panic(err)
}
Expand Down
26 changes: 26 additions & 0 deletions models/awards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package models

type UserAwards struct {
TotalAwardsCount int `json:"TotalAwardsCount"`
HiddenAwardsCount int `json:"HiddenAwardsCount"`
MasteryAwardsCount int `json:"MasteryAwardsCount"`
CompletionAwardsCount int `json:"CompletionAwardsCount"`
BeatenHardcoreAwardsCount int `json:"BeatenHardcoreAwardsCount"`
BeatenSoftcoreAwardsCount int `json:"BeatenSoftcoreAwardsCount"`
EventAwardsCount int `json:"EventAwardsCount"`
SiteAwardsCount int `json:"SiteAwardsCount"`
VisibleUserAwards []Award `json:"VisibleUserAwards"`
}

type Award struct {
AwardedAt RFC3339NumColonTZ `json:"AwardedAt"`
AwardType string `json:"AwardType"`
AwardData int `json:"AwardData"`
AwardDataExtra int `json:"AwardDataExtra"`
DisplayOrder int `json:"DisplayOrder"`
Title *string `json:"Title"`
ConsoleID *int `json:"ConsoleID"`
ConsoleName *string `json:"ConsoleName"`
Flags *int `json:"Flags"`
ImageIcon *string `json:"ImageIcon"`
}
18 changes: 18 additions & 0 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,21 @@ func (c *Client) GetUserCompletionProgress(username string) (*models.UserComplet
}
return completionProgress, nil
}

// GetUserAwards get a list of a user's site awards/badges.
func (c *Client) GetUserAwards(username string) (*models.UserAwards, error) {
resp, err := c.do(
raHttp.Method(http.MethodGet),
raHttp.Path("/API/API_GetUserAwards.php"),
raHttp.APIToken(c.Secret),
raHttp.Username(username),
)
if err != nil {
return nil, fmt.Errorf("calling endpoint: %w", err)
}
awards, err := raHttp.ResponseObject[models.UserAwards](resp)
if err != nil {
return nil, fmt.Errorf("parsing response object: %w", err)
}
return awards, nil
}
155 changes: 155 additions & 0 deletions user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -976,3 +976,158 @@ func TestGetUserCompletionProgress(tt *testing.T) {
})
}
}

func TestGetUserAwards(tt *testing.T) {
awardedAt, err := time.Parse(models.RFC3339NumColonTZFormat, "2024-05-03T23:24:11+00:00")
require.NoError(tt, err)
title := "Pokemon FireRed Version"
consoleID := 5
consoleName := "Game Boy Advance"
flags := 1
imageIcons := "/Images/074224.png"
tests := []struct {
name string
username string
modifyURL func(url string) string
responseCode int
responseUserAwards models.UserAwards
responseError models.ErrorResponse
response func(userAwardsBytes []byte, errorBytes []byte) []byte
assert func(t *testing.T, userAwards *models.UserAwards, err error)
}{
{
name: "fail to call endpoint",
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(userCompletionProgressBytes []byte, errorBytes []byte) []byte {
return errorBytes
},
assert: func(t *testing.T, userAwards *models.UserAwards, err error) {
require.Nil(t, userAwards)
require.EqualError(t, err, "calling endpoint: Get \"/API/API_GetUserAwards.php?u=Test&y=some_secret\": unsupported protocol scheme \"\"")
},
},
{
name: "error response",
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(userCompletionProgressBytes []byte, errorBytes []byte) []byte {
return errorBytes
},
assert: func(t *testing.T, userAwards *models.UserAwards, err error) {
require.Nil(t, userAwards)
require.EqualError(t, err, "parsing response object: error responses: [401] Not Authorized")
},
},
{
name: "success",
username: "Test",
modifyURL: func(url string) string {
return url
},
responseCode: http.StatusOK,
responseUserAwards: models.UserAwards{
TotalAwardsCount: 9,
HiddenAwardsCount: 0,
MasteryAwardsCount: 4,
CompletionAwardsCount: 0,
BeatenHardcoreAwardsCount: 4,
BeatenSoftcoreAwardsCount: 0,
EventAwardsCount: 0,
SiteAwardsCount: 1,
VisibleUserAwards: []models.Award{
{
AwardedAt: models.RFC3339NumColonTZ{
Time: awardedAt,
},
AwardType: "Game Beaten",
AwardData: 515,
AwardDataExtra: 1,
DisplayOrder: 0,
Title: &title,
ConsoleID: &consoleID,
ConsoleName: &consoleName,
Flags: &flags,
ImageIcon: &imageIcons,
},
},
},
response: func(userCompletionProgressBytes []byte, errorBytes []byte) []byte {
return userCompletionProgressBytes
},
assert: func(t *testing.T, userAwards *models.UserAwards, err error) {
require.NotNil(t, userAwards)
require.Equal(t, 9, userAwards.TotalAwardsCount)
require.Equal(t, 0, userAwards.HiddenAwardsCount)
require.Equal(t, 4, userAwards.MasteryAwardsCount)
require.Equal(t, 0, userAwards.CompletionAwardsCount)
require.Equal(t, 4, userAwards.BeatenHardcoreAwardsCount)
require.Equal(t, 0, userAwards.BeatenSoftcoreAwardsCount)
require.Equal(t, 0, userAwards.EventAwardsCount)
require.Equal(t, 1, userAwards.SiteAwardsCount)
require.Len(t, userAwards.VisibleUserAwards, 1)
require.Equal(t, awardedAt, userAwards.VisibleUserAwards[0].AwardedAt.Time)
require.Equal(t, "Game Beaten", userAwards.VisibleUserAwards[0].AwardType)
require.Equal(t, 515, userAwards.VisibleUserAwards[0].AwardData)
require.Equal(t, 1, userAwards.VisibleUserAwards[0].AwardDataExtra)
require.Equal(t, 0, userAwards.VisibleUserAwards[0].DisplayOrder)
require.Equal(t, title, *userAwards.VisibleUserAwards[0].Title)
require.Equal(t, consoleID, *userAwards.VisibleUserAwards[0].ConsoleID)
require.Equal(t, consoleName, *userAwards.VisibleUserAwards[0].ConsoleName)
require.Equal(t, flags, *userAwards.VisibleUserAwards[0].Flags)
require.Equal(t, imageIcons, *userAwards.VisibleUserAwards[0].ImageIcon)
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_GetUserAwards.php"
if r.URL.Path != expectedPath {
t.Errorf("Expected to request '%s', got: %s", expectedPath, r.URL.Path)
}
w.WriteHeader(test.responseCode)
userAwardsBytes, err := json.Marshal(test.responseUserAwards)
require.NoError(t, err)
errBytes, err := json.Marshal(test.responseError)
require.NoError(t, err)
resp := test.response(userAwardsBytes, 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")
userAwards, err := client.GetUserAwards(test.username)
test.assert(t, userAwards, err)
})
}
}

0 comments on commit 856a97d

Please sign in to comment.