Go, Go Context! Added context transfer literally everywhere

This commit is contained in:
ErickSkrauch
2024-02-13 02:08:42 +01:00
parent fdafbc4f0e
commit f5bc474b4d
21 changed files with 209 additions and 246 deletions

View File

@@ -6,14 +6,18 @@ import (
"sync"
"time"
"github.com/SentimensRG/ctx/mergectx"
"ely.by/chrly/internal/utils"
)
type UsernamesToUuidsEndpoint func(ctx context.Context, usernames []string) ([]*ProfileInfo, error)
type BatchUuidsProvider struct {
UsernamesToUuidsEndpoint func(usernames []string) ([]*ProfileInfo, error)
batch int
delay time.Duration
fireOnFull bool
UsernamesToUuidsEndpoint
batch int
delay time.Duration
fireOnFull bool
queue *utils.Queue[*job]
fireChan chan any
@@ -22,7 +26,7 @@ type BatchUuidsProvider struct {
}
func NewBatchUuidsProvider(
endpoint func(usernames []string) ([]*ProfileInfo, error),
endpoint UsernamesToUuidsEndpoint,
batchSize int,
awaitDelay time.Duration,
fireOnFull bool,
@@ -115,12 +119,14 @@ func (p *BatchUuidsProvider) fireRequest() {
return
}
ctx := context.Background()
usernames := make([]string, len(jobs))
for i, job := range jobs {
usernames[i] = job.Username
ctx = mergectx.Join(ctx, job.Ctx)
}
profiles, err := p.UsernamesToUuidsEndpoint(usernames)
profiles, err := p.UsernamesToUuidsEndpoint(ctx, usernames)
for _, job := range jobs {
response := &jobResult{}
if err == nil {

View File

@@ -16,8 +16,8 @@ type mojangUsernamesToUuidsRequestMock struct {
mock.Mock
}
func (o *mojangUsernamesToUuidsRequestMock) UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
args := o.Called(usernames)
func (o *mojangUsernamesToUuidsRequestMock) UsernamesToUuids(ctx context.Context, usernames []string) ([]*ProfileInfo, error) {
args := o.Called(ctx, usernames)
var result []*ProfileInfo
if casted, ok := args.Get(0).([]*ProfileInfo); ok {
result = casted
@@ -81,7 +81,7 @@ func (s *batchUuidsProviderTestSuite) TestGetUuidForFewUsernamesSuccessfully() {
expectedResult1 := &ProfileInfo{Id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Name: "username1"}
expectedResult2 := &ProfileInfo{Id: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Name: "username2"}
s.MojangApi.On("UsernamesToUuids", expectedUsernames).Once().Return([]*ProfileInfo{
s.MojangApi.On("UsernamesToUuids", mock.Anything, expectedUsernames).Once().Return([]*ProfileInfo{
expectedResult1,
expectedResult2,
}, nil)
@@ -110,8 +110,8 @@ func (s *batchUuidsProviderTestSuite) TestGetUuidForFewUsernamesSuccessfully() {
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesSplitByMultipleIterations() {
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", []string{"username1", "username2", "username3"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", []string{"username4"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username1", "username2", "username3"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username4"}).Once().Return(emptyResponse, nil)
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
@@ -133,7 +133,7 @@ func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesSplitByMultiple
func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesWhenOneOfContextIsDeadlined() {
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", []string{"username1", "username2", "username4"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username1", "username2", "username4"}).Once().Return(emptyResponse, nil)
ctx, cancelCtx := context.WithCancel(context.Background())
@@ -167,8 +167,8 @@ func (s *batchUuidsProviderTestSuite) TestGetUuidForManyUsernamesFireOnFull() {
var emptyResponse []string
s.MojangApi.On("UsernamesToUuids", []string{"username1", "username2", "username3"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", []string{"username4"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username1", "username2", "username3"}).Once().Return(emptyResponse, nil)
s.MojangApi.On("UsernamesToUuids", mock.Anything, []string{"username4"}).Once().Return(emptyResponse, nil)
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")
@@ -191,7 +191,7 @@ func (s *batchUuidsProviderTestSuite) TestGetUuidForFewUsernamesWithAnError() {
expectedUsernames := []string{"username1", "username2"}
expectedError := errors.New("mock error")
s.MojangApi.On("UsernamesToUuids", expectedUsernames).Once().Return(nil, expectedError)
s.MojangApi.On("UsernamesToUuids", mock.Anything, expectedUsernames).Once().Return(nil, expectedError)
resultChan1 := s.GetUuidAsync("username1")
resultChan2 := s.GetUuidAsync("username2")

View File

@@ -2,6 +2,7 @@ package mojang
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
@@ -43,9 +44,9 @@ func NewMojangApi(
// Exchanges usernames array to array of uuids
// See https://wiki.vg/Mojang_API#Playernames_-.3E_UUIDs
func (c *MojangApi) UsernamesToUuids(usernames []string) ([]*ProfileInfo, error) {
func (c *MojangApi) UsernamesToUuids(ctx context.Context, usernames []string) ([]*ProfileInfo, error) {
requestBody, _ := json.Marshal(usernames)
request, err := http.NewRequest("POST", c.batchUuidsUrl, bytes.NewBuffer(requestBody))
request, err := http.NewRequestWithContext(ctx, "POST", c.batchUuidsUrl, bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}
@@ -75,14 +76,14 @@ func (c *MojangApi) UsernamesToUuids(usernames []string) ([]*ProfileInfo, error)
// Obtains textures information for provided uuid
// See https://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape
func (c *MojangApi) UuidToTextures(uuid string, signed bool) (*ProfileResponse, error) {
func (c *MojangApi) UuidToTextures(ctx context.Context, uuid string, signed bool) (*ProfileResponse, error) {
normalizedUuid := strings.ReplaceAll(uuid, "-", "")
url := c.profileUrl + normalizedUuid
if signed {
url += "?unsigned=false"
}
request, err := http.NewRequest("GET", url, nil)
request, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}

View File

@@ -1,6 +1,7 @@
package mojang
import (
"context"
"net/http"
"testing"
@@ -44,19 +45,18 @@ func (s *MojangApiSuite) TestUsernamesToUuidsSuccessfully() {
},
})
result, err := s.api.UsernamesToUuids([]string{"Thinkofdeath", "maksimkurb"})
if s.Assert().NoError(err) {
s.Assert().Len(result, 2)
s.Assert().Equal("4566e69fc90748ee8d71d7ba5aa00d20", result[0].Id)
s.Assert().Equal("Thinkofdeath", result[0].Name)
s.Assert().False(result[0].IsLegacy)
s.Assert().True(result[0].IsDemo)
result, err := s.api.UsernamesToUuids(context.Background(), []string{"Thinkofdeath", "maksimkurb"})
s.NoError(err)
s.Len(result, 2)
s.Equal("4566e69fc90748ee8d71d7ba5aa00d20", result[0].Id)
s.Equal("Thinkofdeath", result[0].Name)
s.False(result[0].IsLegacy)
s.True(result[0].IsDemo)
s.Assert().Equal("0d252b7218b648bfb86c2ae476954d32", result[1].Id)
s.Assert().Equal("maksimkurb", result[1].Name)
s.Assert().False(result[1].IsLegacy)
s.Assert().False(result[1].IsDemo)
}
s.Equal("0d252b7218b648bfb86c2ae476954d32", result[1].Id)
s.Equal("maksimkurb", result[1].Name)
s.False(result[1].IsLegacy)
s.False(result[1].IsDemo)
}
func (s *MojangApiSuite) TestUsernamesToUuidsBadRequest() {
@@ -68,10 +68,10 @@ func (s *MojangApiSuite) TestUsernamesToUuidsBadRequest() {
"errorMessage": "profileName can not be null or empty.",
})
result, err := s.api.UsernamesToUuids([]string{""})
s.Assert().Nil(result)
s.Assert().IsType(&BadRequestError{}, err)
s.Assert().EqualError(err, "400 IllegalArgumentException: profileName can not be null or empty.")
result, err := s.api.UsernamesToUuids(context.Background(), []string{""})
s.Nil(result)
s.IsType(&BadRequestError{}, err)
s.EqualError(err, "400 IllegalArgumentException: profileName can not be null or empty.")
}
func (s *MojangApiSuite) TestUsernamesToUuidsForbidden() {
@@ -80,10 +80,10 @@ func (s *MojangApiSuite) TestUsernamesToUuidsForbidden() {
Reply(403).
BodyString("just because")
result, err := s.api.UsernamesToUuids([]string{"Thinkofdeath", "maksimkurb"})
s.Assert().Nil(result)
s.Assert().IsType(&ForbiddenError{}, err)
s.Assert().EqualError(err, "403: Forbidden")
result, err := s.api.UsernamesToUuids(context.Background(), []string{"Thinkofdeath", "maksimkurb"})
s.Nil(result)
s.IsType(&ForbiddenError{}, err)
s.EqualError(err, "403: Forbidden")
}
func (s *MojangApiSuite) TestUsernamesToUuidsTooManyRequests() {
@@ -95,10 +95,10 @@ func (s *MojangApiSuite) TestUsernamesToUuidsTooManyRequests() {
"errorMessage": "The client has sent too many requests within a certain amount of time",
})
result, err := s.api.UsernamesToUuids([]string{"Thinkofdeath", "maksimkurb"})
s.Assert().Nil(result)
s.Assert().IsType(&TooManyRequestsError{}, err)
s.Assert().EqualError(err, "429: Too Many Requests")
result, err := s.api.UsernamesToUuids(context.Background(), []string{"Thinkofdeath", "maksimkurb"})
s.Nil(result)
s.IsType(&TooManyRequestsError{}, err)
s.EqualError(err, "429: Too Many Requests")
}
func (s *MojangApiSuite) TestUsernamesToUuidsServerError() {
@@ -107,11 +107,11 @@ func (s *MojangApiSuite) TestUsernamesToUuidsServerError() {
Reply(500).
BodyString("500 Internal Server Error")
result, err := s.api.UsernamesToUuids([]string{"Thinkofdeath", "maksimkurb"})
s.Assert().Nil(result)
s.Assert().IsType(&ServerError{}, err)
s.Assert().EqualError(err, "500: Server error")
s.Assert().Equal(500, err.(*ServerError).Status)
result, err := s.api.UsernamesToUuids(context.Background(), []string{"Thinkofdeath", "maksimkurb"})
s.Nil(result)
s.IsType(&ServerError{}, err)
s.EqualError(err, "500: Server error")
s.Equal(500, err.(*ServerError).Status)
}
func (s *MojangApiSuite) TestUuidToTexturesSuccessfulResponse() {
@@ -129,14 +129,14 @@ func (s *MojangApiSuite) TestUuidToTexturesSuccessfulResponse() {
},
})
result, err := s.api.UuidToTextures("4566e69fc90748ee8d71d7ba5aa00d20", false)
s.Assert().NoError(err)
s.Assert().Equal("4566e69fc90748ee8d71d7ba5aa00d20", result.Id)
s.Assert().Equal("Thinkofdeath", result.Name)
s.Assert().Equal(1, len(result.Props))
s.Assert().Equal("textures", result.Props[0].Name)
s.Assert().Equal(476, len(result.Props[0].Value))
s.Assert().Equal("", result.Props[0].Signature)
result, err := s.api.UuidToTextures(context.Background(), "4566e69fc90748ee8d71d7ba5aa00d20", false)
s.NoError(err)
s.Equal("4566e69fc90748ee8d71d7ba5aa00d20", result.Id)
s.Equal("Thinkofdeath", result.Name)
s.Equal(1, len(result.Props))
s.Equal("textures", result.Props[0].Name)
s.Equal(476, len(result.Props[0].Value))
s.Equal("", result.Props[0].Signature)
}
func (s *MojangApiSuite) TestUuidToTexturesEmptyResponse() {
@@ -145,9 +145,9 @@ func (s *MojangApiSuite) TestUuidToTexturesEmptyResponse() {
Reply(204).
BodyString("")
result, err := s.api.UuidToTextures("4566e69fc90748ee8d71d7ba5aa00d20", false)
s.Assert().Nil(result)
s.Assert().NoError(err)
result, err := s.api.UuidToTextures(context.Background(), "4566e69fc90748ee8d71d7ba5aa00d20", false)
s.NoError(err)
s.Nil(result)
}
func (s *MojangApiSuite) TestUuidToTexturesTooManyRequests() {
@@ -159,10 +159,10 @@ func (s *MojangApiSuite) TestUuidToTexturesTooManyRequests() {
"errorMessage": "The client has sent too many requests within a certain amount of time",
})
result, err := s.api.UuidToTextures("4566e69fc90748ee8d71d7ba5aa00d20", false)
s.Assert().Nil(result)
s.Assert().IsType(&TooManyRequestsError{}, err)
s.Assert().EqualError(err, "429: Too Many Requests")
result, err := s.api.UuidToTextures(context.Background(), "4566e69fc90748ee8d71d7ba5aa00d20", false)
s.Nil(result)
s.IsType(&TooManyRequestsError{}, err)
s.EqualError(err, "429: Too Many Requests")
}
func (s *MojangApiSuite) TestUuidToTexturesServerError() {
@@ -171,18 +171,18 @@ func (s *MojangApiSuite) TestUuidToTexturesServerError() {
Reply(500).
BodyString("500 Internal Server Error")
result, err := s.api.UuidToTextures("4566e69fc90748ee8d71d7ba5aa00d20", false)
s.Assert().Nil(result)
s.Assert().IsType(&ServerError{}, err)
s.Assert().EqualError(err, "500: Server error")
s.Assert().Equal(500, err.(*ServerError).Status)
result, err := s.api.UuidToTextures(context.Background(), "4566e69fc90748ee8d71d7ba5aa00d20", false)
s.Nil(result)
s.IsType(&ServerError{}, err)
s.EqualError(err, "500: Server error")
s.Equal(500, err.(*ServerError).Status)
}
func TestMojangApi(t *testing.T) {
suite.Run(t, new(MojangApiSuite))
}
func TestSignedTexturesResponse(t *testing.T) {
func TestProfileResponse(t *testing.T) {
t.Run("DecodeTextures", func(t *testing.T) {
obj := &ProfileResponse{
Id: "00000000000000000000000000000000",

View File

@@ -9,11 +9,11 @@ import (
)
type MojangApiTexturesProvider struct {
MojangApiTexturesEndpoint func(uuid string, signed bool) (*ProfileResponse, error)
MojangApiTexturesEndpoint func(ctx context.Context, uuid string, signed bool) (*ProfileResponse, error)
}
func (p *MojangApiTexturesProvider) GetTextures(ctx context.Context, uuid string) (*ProfileResponse, error) {
return p.MojangApiTexturesEndpoint(uuid, true)
return p.MojangApiTexturesEndpoint(ctx, uuid, true)
}
// Perfectly there should be an object with provider and cache implementation,

View File

@@ -34,8 +34,8 @@ type MojangUuidToTexturesRequestMock struct {
mock.Mock
}
func (m *MojangUuidToTexturesRequestMock) UuidToTextures(uuid string, signed bool) (*ProfileResponse, error) {
args := m.Called(uuid, signed)
func (m *MojangUuidToTexturesRequestMock) UuidToTextures(ctx context.Context, uuid string, signed bool) (*ProfileResponse, error) {
args := m.Called(ctx, uuid, signed)
var result *ProfileResponse
if casted, ok := args.Get(0).(*ProfileResponse); ok {
result = casted
@@ -63,9 +63,11 @@ func (s *MojangApiTexturesProviderSuite) TearDownTest() {
}
func (s *MojangApiTexturesProviderSuite) TestGetTextures() {
s.MojangApi.On("UuidToTextures", "dead24f9a4fa4877b7b04c8c6c72bb46", true).Once().Return(signedTexturesResponse, nil)
ctx := context.Background()
result, err := s.Provider.GetTextures(context.Background(), "dead24f9a4fa4877b7b04c8c6c72bb46")
s.MojangApi.On("UuidToTextures", ctx, "dead24f9a4fa4877b7b04c8c6c72bb46", true).Once().Return(signedTexturesResponse, nil)
result, err := s.Provider.GetTextures(ctx, "dead24f9a4fa4877b7b04c8c6c72bb46")
s.Require().NoError(err)
s.Require().Equal(signedTexturesResponse, result)
@@ -73,7 +75,7 @@ func (s *MojangApiTexturesProviderSuite) TestGetTextures() {
func (s *MojangApiTexturesProviderSuite) TestGetTexturesWithError() {
expectedError := errors.New("mock error")
s.MojangApi.On("UuidToTextures", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true).Once().Return(nil, expectedError)
s.MojangApi.On("UuidToTextures", mock.Anything, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true).Once().Return(nil, expectedError)
result, err := s.Provider.GetTextures(context.Background(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

View File

@@ -5,9 +5,9 @@ import "context"
type MojangUuidsStorage interface {
// The second argument must be returned as a incoming username in case,
// when cached result indicates that there is no Mojang user with provided username
GetUuidForMojangUsername(username string) (foundUuid string, foundUsername string, err error)
GetUuidForMojangUsername(ctx context.Context, username string) (foundUuid string, foundUsername string, err error)
// An empty uuid value can be passed if the corresponding account has not been found
StoreMojangUuid(username string, uuid string) error
StoreMojangUuid(ctx context.Context, username string, uuid string) error
}
type UuidsProviderWithCache struct {
@@ -16,7 +16,7 @@ type UuidsProviderWithCache struct {
}
func (p *UuidsProviderWithCache) GetUuid(ctx context.Context, username string) (*ProfileInfo, error) {
uuid, foundUsername, err := p.Storage.GetUuidForMojangUsername(username)
uuid, foundUsername, err := p.Storage.GetUuidForMojangUsername(ctx, username)
if err != nil {
return nil, err
}
@@ -41,7 +41,7 @@ func (p *UuidsProviderWithCache) GetUuid(ctx context.Context, username string) (
wellCasedUsername = profile.Name
}
_ = p.Storage.StoreMojangUuid(wellCasedUsername, freshUuid)
_ = p.Storage.StoreMojangUuid(ctx, wellCasedUsername, freshUuid)
return profile, nil
}

View File

@@ -29,13 +29,13 @@ type MojangUuidsStorageMock struct {
mock.Mock
}
func (m *MojangUuidsStorageMock) GetUuidForMojangUsername(username string) (string, string, error) {
args := m.Called(username)
func (m *MojangUuidsStorageMock) GetUuidForMojangUsername(ctx context.Context, username string) (string, string, error) {
args := m.Called(ctx, username)
return args.String(0), args.String(1), args.Error(2)
}
func (m *MojangUuidsStorageMock) StoreMojangUuid(username string, uuid string) error {
m.Called(username, uuid)
func (m *MojangUuidsStorageMock) StoreMojangUuid(ctx context.Context, username string, uuid string) error {
m.Called(ctx, username, uuid)
return nil
}
@@ -64,8 +64,8 @@ func (s *UuidsProviderWithCacheSuite) TearDownTest() {
func (s *UuidsProviderWithCacheSuite) TestUncachedSuccessfully() {
ctx := context.Background()
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil)
s.Storage.On("StoreMojangUuid", "UserName", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil)
s.Storage.On("GetUuidForMojangUsername", ctx, "username").Return("", "", nil)
s.Storage.On("StoreMojangUuid", ctx, "UserName", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").Once().Return(nil)
s.Original.On("GetUuid", ctx, "username").Once().Return(mockProfile, nil)
@@ -76,8 +76,8 @@ func (s *UuidsProviderWithCacheSuite) TestUncachedSuccessfully() {
}
func (s *UuidsProviderWithCacheSuite) TestUncachedNotExistsMojangUsername() {
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil)
s.Storage.On("StoreMojangUuid", "username", "").Once().Return(nil)
s.Storage.On("GetUuidForMojangUsername", mock.Anything, "username").Return("", "", nil)
s.Storage.On("StoreMojangUuid", mock.Anything, "username", "").Once().Return(nil)
s.Original.On("GetUuid", mock.Anything, "username").Once().Return(nil, nil)
@@ -88,7 +88,7 @@ func (s *UuidsProviderWithCacheSuite) TestUncachedNotExistsMojangUsername() {
}
func (s *UuidsProviderWithCacheSuite) TestKnownCachedUsername() {
s.Storage.On("GetUuidForMojangUsername", "username").Return("mock-uuid", "UserName", nil)
s.Storage.On("GetUuidForMojangUsername", mock.Anything, "username").Return("mock-uuid", "UserName", nil)
result, err := s.Provider.GetUuid(context.Background(), "username")
@@ -99,7 +99,7 @@ func (s *UuidsProviderWithCacheSuite) TestKnownCachedUsername() {
}
func (s *UuidsProviderWithCacheSuite) TestUnknownCachedUsername() {
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "UserName", nil)
s.Storage.On("GetUuidForMojangUsername", mock.Anything, "username").Return("", "UserName", nil)
result, err := s.Provider.GetUuid(context.Background(), "username")
@@ -109,7 +109,7 @@ func (s *UuidsProviderWithCacheSuite) TestUnknownCachedUsername() {
func (s *UuidsProviderWithCacheSuite) TestErrorDuringCacheQuery() {
expectedError := errors.New("mock error")
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", expectedError)
s.Storage.On("GetUuidForMojangUsername", mock.Anything, "username").Return("", "", expectedError)
result, err := s.Provider.GetUuid(context.Background(), "username")
@@ -119,7 +119,7 @@ func (s *UuidsProviderWithCacheSuite) TestErrorDuringCacheQuery() {
func (s *UuidsProviderWithCacheSuite) TestErrorFromOriginalProvider() {
expectedError := errors.New("mock error")
s.Storage.On("GetUuidForMojangUsername", "username").Return("", "", nil)
s.Storage.On("GetUuidForMojangUsername", mock.Anything, "username").Return("", "", nil)
s.Original.On("GetUuid", mock.Anything, "username").Once().Return(nil, expectedError)