#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"` 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) { func UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
requestBody, _ := json.Marshal(usernames) requestBody, _ := json.Marshal(usernames)
request, err := http.NewRequest("POST", "https://api.mojang.com/profiles/minecraft", bytes.NewBuffer(requestBody)) 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() defer response.Body.Close()
if response.StatusCode == 429 { if responseErr := validateResponse(response); responseErr != nil {
return nil, &TooManyRequestsError{} return nil, responseErr
} }
var result []*ProfileInfo var result []*ProfileInfo
@ -55,6 +57,8 @@ func UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
return result, nil 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) { func UuidToTextures(uuid string, signed bool) (*SignedTexturesResponse, error) {
url := "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid url := "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid
if signed { if signed {
@ -72,8 +76,8 @@ func UuidToTextures(uuid string, signed bool) (*SignedTexturesResponse, error) {
} }
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode == 429 { if responseErr := validateResponse(response); responseErr != nil {
return nil, &TooManyRequestsError{} return nil, responseErr
} }
var result *SignedTexturesResponse var result *SignedTexturesResponse
@ -84,9 +88,30 @@ func UuidToTextures(uuid string, signed bool) (*SignedTexturesResponse, error) {
return result, nil 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 { type TooManyRequestsError struct {
} }
func (e *TooManyRequestsError) Error() string { func (*TooManyRequestsError) Error() string {
return "Too Many Requests" 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) assert := testify.New(t)
defer gock.Off() defer gock.Off()

View File

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

View File

@ -293,7 +293,7 @@ func (suite *QueueTestSuite) TestDoNothingWhenNoTasks() {
suite.Iterate() suite.Iterate()
} }
func (suite *QueueTestSuite) TestHandle429ResponseWhenExchangingUsernamesToUuids() { func (suite *QueueTestSuite) TestHandleTooManyRequestsResponseWhenExchangingUsernamesToUuids() {
suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{}) suite.Storage.On("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
// Storage.StoreUuid, Storage.GetTextures and Storage.StoreTextures shouldn't be called // Storage.StoreUuid, Storage.GetTextures and Storage.StoreTextures shouldn't be called
suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return(nil, &mojang.TooManyRequestsError{}) suite.MojangApi.On("UsernameToUuids", []string{"maksimkurb"}).Once().Return(nil, &mojang.TooManyRequestsError{})
@ -305,7 +305,27 @@ func (suite *QueueTestSuite) TestHandle429ResponseWhenExchangingUsernamesToUuids
suite.Assert().Nil(<-resultChan) 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("GetUuid", "maksimkurb").Once().Return("", &ValueNotFound{})
suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once() suite.Storage.On("StoreUuid", "maksimkurb", "0d252b7218b648bfb86c2ae476954d32").Once()
suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{}) suite.Storage.On("GetTextures", "0d252b7218b648bfb86c2ae476954d32").Once().Return(nil, &ValueNotFound{})