#1: Add case when Mojang's API returns empty response

This commit is contained in:
ErickSkrauch 2019-04-20 22:39:17 +03:00
parent e7c0fac346
commit c2921400b0
4 changed files with 78 additions and 13 deletions

View File

@ -28,6 +28,8 @@ type ProfileInfo struct {
IsDemo bool `json:"demo,omitempty"`
}
// Exchanges usernames array to array of uuids
// See https://wiki.vg/Mojang_API#Playernames_-.3E_UUIDs
func UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
requestBody, _ := json.Marshal(usernames)
request, err := http.NewRequest("POST", "https://api.mojang.com/profiles/minecraft", bytes.NewBuffer(requestBody))
@ -43,8 +45,8 @@ func UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
}
defer response.Body.Close()
if response.StatusCode == 429 {
return nil, &TooManyRequestsError{}
if responseErr := validateResponse(response); responseErr != nil {
return nil, responseErr
}
var result []*ProfileInfo
@ -55,6 +57,8 @@ func UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
return result, nil
}
// Obtains textures information for provided uuid
// See https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape
func UuidToTextures(uuid string, signed bool) (*SignedTexturesResponse, error) {
url := "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid
if signed {
@ -72,8 +76,8 @@ func UuidToTextures(uuid string, signed bool) (*SignedTexturesResponse, error) {
}
defer response.Body.Close()
if response.StatusCode == 429 {
return nil, &TooManyRequestsError{}
if responseErr := validateResponse(response); responseErr != nil {
return nil, responseErr
}
var result *SignedTexturesResponse
@ -84,9 +88,30 @@ func UuidToTextures(uuid string, signed bool) (*SignedTexturesResponse, error) {
return result, nil
}
func validateResponse(response *http.Response) error {
switch response.StatusCode {
case 204:
return &EmptyResponse{}
case 429:
return &TooManyRequestsError{}
}
return nil
}
// Mojang API doesn't return a 404 Not Found error for non-existent data identifiers
// Instead, they return 204 with an empty body
type EmptyResponse struct {
}
func (*EmptyResponse) Error() string {
return "Empty Response"
}
// When you exceed the set limit of requests, this error will be returned
type TooManyRequestsError struct {
}
func (e *TooManyRequestsError) Error() string {
func (*TooManyRequestsError) Error() string {
return "Too Many Requests"
}

View File

@ -146,7 +146,27 @@ func TestUuidToTextures(t *testing.T) {
}
})
t.Run("handle too many requests error", func(t *testing.T) {
t.Run("handle empty response", func(t *testing.T) {
assert := testify.New(t)
defer gock.Off()
gock.New("https://sessionserver.mojang.com").
Get("/session/minecraft/profile/4566e69fc90748ee8d71d7ba5aa00d20").
Reply(204).
BodyString("")
client := &http.Client{}
gock.InterceptClient(client)
HttpClient = client
result, err := UuidToTextures("4566e69fc90748ee8d71d7ba5aa00d20", false)
assert.Nil(result)
assert.IsType(&EmptyResponse{}, err)
assert.EqualError(err, "Empty Response")
})
t.Run("handle too many requests response", func(t *testing.T) {
assert := testify.New(t)
defer gock.Off()

View File

@ -146,12 +146,12 @@ func (ctx *JobsQueue) getTextures(uuid string) *mojang.SignedTexturesResponse {
shouldCache := true
result, err := uuidToTextures(uuid, true)
if err != nil {
if _, ok := err.(*mojang.TooManyRequestsError); !ok {
panic(err)
}
switch err.(type) {
case *mojang.EmptyResponse:
case *mojang.TooManyRequestsError:
shouldCache = false
case error:
panic(err)
}
if shouldCache && result != nil {

View File

@ -293,7 +293,7 @@ func (suite *QueueTestSuite) TestDoNothingWhenNoTasks() {
suite.Iterate()
}
func (suite *QueueTestSuite) TestHandle429ResponseWhenExchangingUsernamesToUuids() {
func (suite *QueueTestSuite) TestHandleTooManyRequestsResponseWhenExchangingUsernamesToUuids() {
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
// Storage.StoreUuid, Storage.GetTextures and Storage.StoreTextures shouldn't be called
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return(nil, &mojang.TooManyRequestsError{})
@ -305,7 +305,27 @@ func (suite *QueueTestSuite) TestHandle429ResponseWhenExchangingUsernamesToUuids
suite.Assert().Nil(<-resultChan)
}
func (suite *QueueTestSuite) TestHandle429ResponseWhenRequestingUsersTextures() {
func (suite *QueueTestSuite) TestHandleEmptyResponseWhenRequestingUsersTextures() {
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})
// Storage.StoreTextures shouldn't be called
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return([]*mojang.ProfileInfo{
{Id: "0d252b7218b648bfb86c2ae476954d32", Name: "maksimkurb"},
}, nil)
suite.MojangApi.On("UuidToTextures", "0d252b7218b648bfb86c2ae476954d32", true).Once().Return(
nil,
&mojang.EmptyResponse{},
)
resultChan := suite.Queue.GetTexturesForUsername("maksimkurb")
suite.Iterate()
suite.Assert().Nil(<-resultChan)
}
func (suite *QueueTestSuite) TestHandleTooManyRequestsResponseWhenRequestingUsersTextures() {
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})