mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	Go, Go Context! Added context transfer literally everywhere
This commit is contained in:
		| @@ -15,7 +15,6 @@ const userUuidToUsernameKey = "hash:uuid-to-username" | ||||
|  | ||||
| type Redis struct { | ||||
| 	client     radix.Client | ||||
| 	context    context.Context | ||||
| 	serializer db.ProfileSerializer | ||||
| } | ||||
|  | ||||
| @@ -27,14 +26,13 @@ func New(ctx context.Context, profileSerializer db.ProfileSerializer, addr strin | ||||
|  | ||||
| 	return &Redis{ | ||||
| 		client:     client, | ||||
| 		context:    ctx, | ||||
| 		serializer: profileSerializer, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (r *Redis) FindProfileByUsername(username string) (*db.Profile, error) { | ||||
| func (r *Redis) FindProfileByUsername(ctx context.Context, username string) (*db.Profile, error) { | ||||
| 	var profile *db.Profile | ||||
| 	err := r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 	err := r.client.Do(ctx, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 		var err error | ||||
| 		profile, err = r.findProfileByUsername(ctx, conn, username) | ||||
|  | ||||
| @@ -58,38 +56,13 @@ func (r *Redis) findProfileByUsername(ctx context.Context, conn radix.Conn, user | ||||
| 	return r.serializer.Deserialize(encodedResult) | ||||
| } | ||||
|  | ||||
| func (r *Redis) FindProfileByUuid(uuid string) (*db.Profile, error) { | ||||
| 	var skin *db.Profile | ||||
| 	err := r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 		var err error | ||||
| 		skin, err = r.findProfileByUuid(ctx, conn, uuid) | ||||
|  | ||||
| 		return err | ||||
| 	})) | ||||
|  | ||||
| 	return skin, err | ||||
| } | ||||
|  | ||||
| func (r *Redis) findProfileByUuid(ctx context.Context, conn radix.Conn, uuid string) (*db.Profile, error) { | ||||
| 	username, err := r.findUsernameHashKeyByUuid(ctx, conn, uuid) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if username == "" { | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	return r.findProfileByUsername(ctx, conn, username) | ||||
| } | ||||
|  | ||||
| func (r *Redis) findUsernameHashKeyByUuid(ctx context.Context, conn radix.Conn, uuid string) (string, error) { | ||||
| 	var username string | ||||
| 	return username, conn.Do(ctx, radix.FlatCmd(&username, "HGET", userUuidToUsernameKey, normalizeUuid(uuid))) | ||||
| } | ||||
|  | ||||
| func (r *Redis) SaveProfile(profile *db.Profile) error { | ||||
| 	return r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| func (r *Redis) SaveProfile(ctx context.Context, profile *db.Profile) error { | ||||
| 	return r.client.Do(ctx, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 		return r.saveProfile(ctx, conn, profile) | ||||
| 	})) | ||||
| } | ||||
| @@ -137,8 +110,8 @@ func (r *Redis) saveProfile(ctx context.Context, conn radix.Conn, profile *db.Pr | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *Redis) RemoveProfileByUuid(uuid string) error { | ||||
| 	return r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| func (r *Redis) RemoveProfileByUuid(ctx context.Context, uuid string) error { | ||||
| 	return r.client.Do(ctx, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 		return r.removeProfileByUuid(ctx, conn, uuid) | ||||
| 	})) | ||||
| } | ||||
| @@ -169,10 +142,10 @@ func (r *Redis) removeProfileByUuid(ctx context.Context, conn radix.Conn, uuid s | ||||
| 	return conn.Do(ctx, radix.Cmd(nil, "EXEC")) | ||||
| } | ||||
|  | ||||
| func (r *Redis) GetUuidForMojangUsername(username string) (string, string, error) { | ||||
| func (r *Redis) GetUuidForMojangUsername(ctx context.Context, username string) (string, string, error) { | ||||
| 	var uuid string | ||||
| 	foundUsername := username | ||||
| 	err := r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 	err := r.client.Do(ctx, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 		var err error | ||||
| 		uuid, foundUsername, err = findMojangUuidByUsername(ctx, conn, username) | ||||
|  | ||||
| @@ -199,8 +172,8 @@ func findMojangUuidByUsername(ctx context.Context, conn radix.Conn, username str | ||||
| 	return parts[1], parts[0], nil | ||||
| } | ||||
|  | ||||
| func (r *Redis) StoreMojangUuid(username string, uuid string) error { | ||||
| 	return r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| func (r *Redis) StoreMojangUuid(ctx context.Context, username string, uuid string) error { | ||||
| 	return r.client.Do(ctx, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error { | ||||
| 		return storeMojangUuid(ctx, conn, username, uuid) | ||||
| 	})) | ||||
| } | ||||
|   | ||||
| @@ -115,19 +115,20 @@ func TestRedis(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func (s *redisTestSuite) TestFindProfileByUsername() { | ||||
| 	ctx := context.Background() | ||||
| 	s.Run("exists record", func() { | ||||
| 		serializedData := []byte("mock.exists.profile") | ||||
| 		expectedProfile := &db.Profile{} | ||||
| 		s.cmd("HSET", usernameToProfileKey, "mock", serializedData) | ||||
| 		s.Serializer.On("Deserialize", serializedData).Return(expectedProfile, nil) | ||||
|  | ||||
| 		profile, err := s.Redis.FindProfileByUsername("Mock") | ||||
| 		profile, err := s.Redis.FindProfileByUsername(ctx, "Mock") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Same(expectedProfile, profile) | ||||
| 	}) | ||||
|  | ||||
| 	s.Run("not exists record", func() { | ||||
| 		profile, err := s.Redis.FindProfileByUsername("Mock") | ||||
| 		profile, err := s.Redis.FindProfileByUsername(ctx, "Mock") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Nil(profile) | ||||
| 	}) | ||||
| @@ -137,40 +138,15 @@ func (s *redisTestSuite) TestFindProfileByUsername() { | ||||
| 		s.cmd("HSET", usernameToProfileKey, "mock", "some-invalid-mock-data") | ||||
| 		s.Serializer.On("Deserialize", mock.Anything).Return(nil, expectedError) | ||||
|  | ||||
| 		profile, err := s.Redis.FindProfileByUsername("Mock") | ||||
| 		profile, err := s.Redis.FindProfileByUsername(ctx, "Mock") | ||||
| 		s.Require().Nil(profile) | ||||
| 		s.Require().ErrorIs(err, expectedError) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (s *redisTestSuite) TestFindProfileByUuid() { | ||||
| 	s.Run("exists record", func() { | ||||
| 		serializedData := []byte("mock.exists.profile") | ||||
| 		expectedProfile := &db.Profile{Username: "Mock"} | ||||
| 		s.cmd("HSET", usernameToProfileKey, "mock", serializedData) | ||||
| 		s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock") | ||||
| 		s.Serializer.On("Deserialize", serializedData).Return(expectedProfile, nil) | ||||
|  | ||||
| 		profile, err := s.Redis.FindProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Same(expectedProfile, profile) | ||||
| 	}) | ||||
|  | ||||
| 	s.Run("not exists record", func() { | ||||
| 		profile, err := s.Redis.FindProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Nil(profile) | ||||
| 	}) | ||||
|  | ||||
| 	s.Run("exists uuid record, but related profile not exists", func() { | ||||
| 		s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock") | ||||
| 		profile, err := s.Redis.FindProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Nil(profile) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (s *redisTestSuite) TestSaveProfile() { | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	s.Run("save new entity", func() { | ||||
| 		profile := &db.Profile{ | ||||
| 			Uuid:     "f57f36d5-4f50-4728-948a-42d5d80b18f3", | ||||
| @@ -182,7 +158,7 @@ func (s *redisTestSuite) TestSaveProfile() { | ||||
| 		s.cmd("HSET", usernameToProfileKey, "mock", serializedProfile) | ||||
| 		s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock") | ||||
|  | ||||
| 		err := s.Redis.SaveProfile(profile) | ||||
| 		err := s.Redis.SaveProfile(ctx, profile) | ||||
| 		s.Require().NoError(err) | ||||
|  | ||||
| 		uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3") | ||||
| @@ -203,7 +179,7 @@ func (s *redisTestSuite) TestSaveProfile() { | ||||
| 		s.cmd("HSET", usernameToProfileKey, "mock", "serialized-old-profile") | ||||
| 		s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock") | ||||
|  | ||||
| 		err := s.Redis.SaveProfile(newProfile) | ||||
| 		err := s.Redis.SaveProfile(ctx, newProfile) | ||||
| 		s.Require().NoError(err) | ||||
|  | ||||
| 		uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3") | ||||
| @@ -218,11 +194,13 @@ func (s *redisTestSuite) TestSaveProfile() { | ||||
| } | ||||
|  | ||||
| func (s *redisTestSuite) TestRemoveProfileByUuid() { | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	s.Run("exists record", func() { | ||||
| 		s.cmd("HSET", usernameToProfileKey, "mock", "serialized-profile") | ||||
| 		s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock") | ||||
|  | ||||
| 		err := s.Redis.RemoveProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		err := s.Redis.RemoveProfileByUuid(ctx, "f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		s.Require().NoError(err) | ||||
|  | ||||
| 		uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3") | ||||
| @@ -235,7 +213,7 @@ func (s *redisTestSuite) TestRemoveProfileByUuid() { | ||||
| 	s.Run("uuid exists, username is missing", func() { | ||||
| 		s.cmd("HSET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3", "mock") | ||||
|  | ||||
| 		err := s.Redis.RemoveProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		err := s.Redis.RemoveProfileByUuid(ctx, "f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		s.Require().NoError(err) | ||||
|  | ||||
| 		uuidResp := s.cmd("HGET", userUuidToUsernameKey, "f57f36d54f504728948a42d5d80b18f3") | ||||
| @@ -243,16 +221,18 @@ func (s *redisTestSuite) TestRemoveProfileByUuid() { | ||||
| 	}) | ||||
|  | ||||
| 	s.Run("uuid not exists", func() { | ||||
| 		err := s.Redis.RemoveProfileByUuid("f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		err := s.Redis.RemoveProfileByUuid(ctx, "f57f36d5-4f50-4728-948a-42d5d80b18f3") | ||||
| 		s.Require().NoError(err) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (s *redisTestSuite) TestGetUuidForMojangUsername() { | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	s.Run("exists record", func() { | ||||
| 		s.cmd("SET", "mojang:uuid:mock", "MoCk:d3ca513eb3e14946b58047f2bd3530fd") | ||||
|  | ||||
| 		uuid, username, err := s.Redis.GetUuidForMojangUsername("Mock") | ||||
| 		uuid, username, err := s.Redis.GetUuidForMojangUsername(ctx, "Mock") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Equal("MoCk", username) | ||||
| 		s.Require().Equal("d3ca513eb3e14946b58047f2bd3530fd", uuid) | ||||
| @@ -261,14 +241,14 @@ func (s *redisTestSuite) TestGetUuidForMojangUsername() { | ||||
| 	s.Run("exists record with empty uuid value", func() { | ||||
| 		s.cmd("SET", "mojang:uuid:mock", "MoCk:") | ||||
|  | ||||
| 		uuid, username, err := s.Redis.GetUuidForMojangUsername("Mock") | ||||
| 		uuid, username, err := s.Redis.GetUuidForMojangUsername(ctx, "Mock") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Equal("MoCk", username) | ||||
| 		s.Require().Empty(uuid) | ||||
| 	}) | ||||
|  | ||||
| 	s.Run("not exists record", func() { | ||||
| 		uuid, username, err := s.Redis.GetUuidForMojangUsername("Mock") | ||||
| 		uuid, username, err := s.Redis.GetUuidForMojangUsername(ctx, "Mock") | ||||
| 		s.Require().NoError(err) | ||||
| 		s.Require().Empty(username) | ||||
| 		s.Require().Empty(uuid) | ||||
| @@ -276,8 +256,10 @@ func (s *redisTestSuite) TestGetUuidForMojangUsername() { | ||||
| } | ||||
|  | ||||
| func (s *redisTestSuite) TestStoreUuid() { | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	s.Run("store uuid", func() { | ||||
| 		err := s.Redis.StoreMojangUuid("MoCk", "d3ca513eb3e14946b58047f2bd3530fd") | ||||
| 		err := s.Redis.StoreMojangUuid(ctx, "MoCk", "d3ca513eb3e14946b58047f2bd3530fd") | ||||
| 		s.Require().NoError(err) | ||||
|  | ||||
| 		resp := s.cmd("GET", "mojang:uuid:mock") | ||||
| @@ -285,7 +267,7 @@ func (s *redisTestSuite) TestStoreUuid() { | ||||
| 	}) | ||||
|  | ||||
| 	s.Run("store empty uuid", func() { | ||||
| 		err := s.Redis.StoreMojangUuid("MoCk", "") | ||||
| 		err := s.Redis.StoreMojangUuid(ctx, "MoCk", "") | ||||
| 		s.Require().NoError(err) | ||||
|  | ||||
| 		resp := s.cmd("GET", "mojang:uuid:mock") | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| @@ -12,8 +13,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| type ProfilesManager interface { | ||||
| 	PersistProfile(profile *db.Profile) error | ||||
| 	RemoveProfileByUuid(uuid string) error | ||||
| 	PersistProfile(ctx context.Context, profile *db.Profile) error | ||||
| 	RemoveProfileByUuid(ctx context.Context, uuid string) error | ||||
| } | ||||
|  | ||||
| type Api struct { | ||||
| @@ -47,7 +48,7 @@ func (ctx *Api) postProfileHandler(resp http.ResponseWriter, req *http.Request) | ||||
| 		MojangSignature: req.Form.Get("mojangSignature"), | ||||
| 	} | ||||
|  | ||||
| 	err = ctx.PersistProfile(profile) | ||||
| 	err = ctx.PersistProfile(req.Context(), profile) | ||||
| 	if err != nil { | ||||
| 		var v *profiles.ValidationError | ||||
| 		if errors.As(err, &v) { | ||||
| @@ -64,7 +65,7 @@ func (ctx *Api) postProfileHandler(resp http.ResponseWriter, req *http.Request) | ||||
|  | ||||
| func (ctx *Api) deleteProfileByUuidHandler(resp http.ResponseWriter, req *http.Request) { | ||||
| 	uuid := mux.Vars(req)["uuid"] | ||||
| 	err := ctx.ProfilesManager.RemoveProfileByUuid(uuid) | ||||
| 	err := ctx.ProfilesManager.RemoveProfileByUuid(req.Context(), uuid) | ||||
| 	if err != nil { | ||||
| 		apiServerError(resp, fmt.Errorf("unable to delete profile from db: %w", err)) | ||||
| 		return | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package http | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| @@ -21,12 +22,12 @@ type ProfilesManagerMock struct { | ||||
| 	mock.Mock | ||||
| } | ||||
|  | ||||
| func (m *ProfilesManagerMock) PersistProfile(profile *db.Profile) error { | ||||
| 	return m.Called(profile).Error(0) | ||||
| func (m *ProfilesManagerMock) PersistProfile(ctx context.Context, profile *db.Profile) error { | ||||
| 	return m.Called(ctx, profile).Error(0) | ||||
| } | ||||
|  | ||||
| func (m *ProfilesManagerMock) RemoveProfileByUuid(uuid string) error { | ||||
| 	return m.Called(uuid).Error(0) | ||||
| func (m *ProfilesManagerMock) RemoveProfileByUuid(ctx context.Context, uuid string) error { | ||||
| 	return m.Called(ctx, uuid).Error(0) | ||||
| } | ||||
|  | ||||
| type ApiTestSuite struct { | ||||
| @@ -50,7 +51,7 @@ func (t *ApiTestSuite) TearDownSubTest() { | ||||
|  | ||||
| func (t *ApiTestSuite) TestPostProfile() { | ||||
| 	t.Run("successfully post profile", func() { | ||||
| 		t.ProfilesManager.On("PersistProfile", &db.Profile{ | ||||
| 		t.ProfilesManager.On("PersistProfile", mock.Anything, &db.Profile{ | ||||
| 			Uuid:            "0f657aa8-bfbe-415d-b700-5750090d3af3", | ||||
| 			Username:        "mock_username", | ||||
| 			SkinUrl:         "https://example.com/skin.png", | ||||
| @@ -100,7 +101,7 @@ func (t *ApiTestSuite) TestPostProfile() { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("receive validation errors", func() { | ||||
| 		t.ProfilesManager.On("PersistProfile", mock.Anything).Once().Return(&profiles.ValidationError{ | ||||
| 		t.ProfilesManager.On("PersistProfile", mock.Anything, mock.Anything).Once().Return(&profiles.ValidationError{ | ||||
| 			Errors: map[string][]string{ | ||||
| 				"mock": {"error1", "error2"}, | ||||
| 			}, | ||||
| @@ -126,7 +127,7 @@ func (t *ApiTestSuite) TestPostProfile() { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("receive other error", func() { | ||||
| 		t.ProfilesManager.On("PersistProfile", mock.Anything).Once().Return(errors.New("mock error")) | ||||
| 		t.ProfilesManager.On("PersistProfile", mock.Anything, mock.Anything).Once().Return(errors.New("mock error")) | ||||
|  | ||||
| 		req := httptest.NewRequest("POST", "http://chrly/profiles", strings.NewReader("")) | ||||
| 		req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | ||||
| @@ -141,7 +142,7 @@ func (t *ApiTestSuite) TestPostProfile() { | ||||
|  | ||||
| func (t *ApiTestSuite) TestDeleteProfileByUuid() { | ||||
| 	t.Run("successfully delete", func() { | ||||
| 		t.ProfilesManager.On("RemoveProfileByUuid", "0f657aa8-bfbe-415d-b700-5750090d3af3").Once().Return(nil) | ||||
| 		t.ProfilesManager.On("RemoveProfileByUuid", mock.Anything, "0f657aa8-bfbe-415d-b700-5750090d3af3").Once().Return(nil) | ||||
|  | ||||
| 		req := httptest.NewRequest("DELETE", "http://chrly/profiles/0f657aa8-bfbe-415d-b700-5750090d3af3", nil) | ||||
| 		w := httptest.NewRecorder() | ||||
| @@ -155,7 +156,7 @@ func (t *ApiTestSuite) TestDeleteProfileByUuid() { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("error from manager", func() { | ||||
| 		t.ProfilesManager.On("RemoveProfileByUuid", mock.Anything).Return(errors.New("mock error")) | ||||
| 		t.ProfilesManager.On("RemoveProfileByUuid", mock.Anything, mock.Anything).Return(errors.New("mock error")) | ||||
|  | ||||
| 		req := httptest.NewRequest("DELETE", "http://chrly/profiles/0f657aa8-bfbe-415d-b700-5750090d3af3", nil) | ||||
| 		w := httptest.NewRecorder() | ||||
|   | ||||
| @@ -25,9 +25,10 @@ type ProfilesProvider interface { | ||||
| 	FindProfileByUsername(ctx context.Context, username string, allowProxy bool) (*db.Profile, error) | ||||
| } | ||||
|  | ||||
| // TexturesSigner uses context because in the future we may separate this logic into a separate microservice | ||||
| type TexturesSigner interface { | ||||
| 	SignTextures(textures string) (string, error) | ||||
| 	GetPublicKey() (*rsa.PublicKey, error) | ||||
| 	SignTextures(ctx context.Context, textures string) (string, error) | ||||
| 	GetPublicKey(ctx context.Context) (*rsa.PublicKey, error) | ||||
| } | ||||
|  | ||||
| type Skinsystem struct { | ||||
| @@ -202,7 +203,7 @@ func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *htt | ||||
| 	} | ||||
|  | ||||
| 	if request.URL.Query().Has("unsigned") && !getToBool(request.URL.Query().Get("unsigned")) { | ||||
| 		signature, err := ctx.TexturesSigner.SignTextures(texturesProp.Value) | ||||
| 		signature, err := ctx.TexturesSigner.SignTextures(request.Context(), texturesProp.Value) | ||||
| 		if err != nil { | ||||
| 			apiServerError(response, fmt.Errorf("unable to sign textures: %w", err)) | ||||
| 			return | ||||
| @@ -229,7 +230,7 @@ func (ctx *Skinsystem) profileHandler(response http.ResponseWriter, request *htt | ||||
| } | ||||
|  | ||||
| func (ctx *Skinsystem) signatureVerificationKeyHandler(response http.ResponseWriter, request *http.Request) { | ||||
| 	publicKey, err := ctx.TexturesSigner.GetPublicKey() | ||||
| 	publicKey, err := ctx.TexturesSigner.GetPublicKey(request.Context()) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|   | ||||
| @@ -38,13 +38,13 @@ type TexturesSignerMock struct { | ||||
| 	mock.Mock | ||||
| } | ||||
|  | ||||
| func (m *TexturesSignerMock) SignTextures(textures string) (string, error) { | ||||
| 	args := m.Called(textures) | ||||
| func (m *TexturesSignerMock) SignTextures(ctx context.Context, textures string) (string, error) { | ||||
| 	args := m.Called(ctx, textures) | ||||
| 	return args.String(0), args.Error(1) | ||||
| } | ||||
|  | ||||
| func (m *TexturesSignerMock) GetPublicKey() (*rsa.PublicKey, error) { | ||||
| 	args := m.Called() | ||||
| func (m *TexturesSignerMock) GetPublicKey(ctx context.Context) (*rsa.PublicKey, error) { | ||||
| 	args := m.Called(ctx) | ||||
| 	var publicKey *rsa.PublicKey | ||||
| 	if casted, ok := args.Get(0).(*rsa.PublicKey); ok { | ||||
| 		publicKey = casted | ||||
| @@ -470,7 +470,7 @@ func (t *SkinsystemTestSuite) TestProfile() { | ||||
| 			SkinUrl:   "https://example.com/skin.png", | ||||
| 			SkinModel: "slim", | ||||
| 		}, nil) | ||||
| 		t.TexturesSigner.On("SignTextures", "eyJ0aW1lc3RhbXAiOjE2MTQyMTQyMjMwMDAsInByb2ZpbGVJZCI6Im1vY2stdXVpZCIsInByb2ZpbGVOYW1lIjoibW9ja191c2VybmFtZSIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9za2luLnBuZyIsIm1ldGFkYXRhIjp7Im1vZGVsIjoic2xpbSJ9fX19").Return("mock signature", nil) | ||||
| 		t.TexturesSigner.On("SignTextures", mock.Anything, "eyJ0aW1lc3RhbXAiOjE2MTQyMTQyMjMwMDAsInByb2ZpbGVJZCI6Im1vY2stdXVpZCIsInByb2ZpbGVOYW1lIjoibW9ja191c2VybmFtZSIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9za2luLnBuZyIsIm1ldGFkYXRhIjp7Im1vZGVsIjoic2xpbSJ9fX19").Return("mock signature", nil) | ||||
|  | ||||
| 		t.App.Handler().ServeHTTP(w, req) | ||||
|  | ||||
| @@ -526,7 +526,7 @@ func (t *SkinsystemTestSuite) TestProfile() { | ||||
| 		w := httptest.NewRecorder() | ||||
|  | ||||
| 		t.ProfilesProvider.On("FindProfileByUsername", mock.Anything, "mock_username", true).Return(&db.Profile{}, nil) | ||||
| 		t.TexturesSigner.On("SignTextures", mock.Anything).Return("", errors.New("mock error")) | ||||
| 		t.TexturesSigner.On("SignTextures", mock.Anything, mock.Anything).Return("", errors.New("mock error")) | ||||
|  | ||||
| 		t.App.Handler().ServeHTTP(w, req) | ||||
|  | ||||
| @@ -551,7 +551,7 @@ var signingKeyTestsCases = []*signingKeyTestCase{ | ||||
| 			pubPem, _ := pem.Decode([]byte("-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbUpVCZkMKpfvYZ08W3lumdAaYxLBnm\nUDlzHBQH3DpYef5WCO32TDU6feIJ58A0lAywgtZ4wwi2dGHOz/1hAvcCAwEAAQ==\n-----END PUBLIC KEY-----")) | ||||
| 			publicKey, _ := x509.ParsePKIXPublicKey(pubPem.Bytes) | ||||
|  | ||||
| 			suite.TexturesSigner.On("GetPublicKey").Return(publicKey, nil) | ||||
| 			suite.TexturesSigner.On("GetPublicKey", mock.Anything).Return(publicKey, nil) | ||||
| 		}, | ||||
| 		AfterTest: func(suite *SkinsystemTestSuite, response *http.Response) { | ||||
| 			suite.Equal(200, response.StatusCode) | ||||
| @@ -568,7 +568,7 @@ var signingKeyTestsCases = []*signingKeyTestCase{ | ||||
| 			pubPem, _ := pem.Decode([]byte("-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbUpVCZkMKpfvYZ08W3lumdAaYxLBnm\nUDlzHBQH3DpYef5WCO32TDU6feIJ58A0lAywgtZ4wwi2dGHOz/1hAvcCAwEAAQ==\n-----END PUBLIC KEY-----")) | ||||
| 			publicKey, _ := x509.ParsePKIXPublicKey(pubPem.Bytes) | ||||
|  | ||||
| 			suite.TexturesSigner.On("GetPublicKey").Return(publicKey, nil) | ||||
| 			suite.TexturesSigner.On("GetPublicKey", mock.Anything).Return(publicKey, nil) | ||||
| 		}, | ||||
| 		AfterTest: func(suite *SkinsystemTestSuite, response *http.Response) { | ||||
| 			suite.Equal(200, response.StatusCode) | ||||
| @@ -582,7 +582,7 @@ var signingKeyTestsCases = []*signingKeyTestCase{ | ||||
| 		Name:      "Error while obtaining public key", | ||||
| 		KeyFormat: "DER", | ||||
| 		BeforeTest: func(suite *SkinsystemTestSuite) { | ||||
| 			suite.TexturesSigner.On("GetPublicKey").Return(nil, errors.New("textures signer error")) | ||||
| 			suite.TexturesSigner.On("GetPublicKey", mock.Anything).Return(nil, errors.New("textures signer error")) | ||||
| 		}, | ||||
| 		PanicErr: "textures signer error", | ||||
| 	}, | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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") | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package profiles | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| @@ -11,9 +12,8 @@ import ( | ||||
| ) | ||||
|  | ||||
| type ProfilesRepository interface { | ||||
| 	FindProfileByUuid(uuid string) (*db.Profile, error) | ||||
| 	SaveProfile(profile *db.Profile) error | ||||
| 	RemoveProfileByUuid(uuid string) error | ||||
| 	SaveProfile(ctx context.Context, profile *db.Profile) error | ||||
| 	RemoveProfileByUuid(ctx context.Context, uuid string) error | ||||
| } | ||||
|  | ||||
| func NewManager(pr ProfilesRepository) *Manager { | ||||
| @@ -28,7 +28,7 @@ type Manager struct { | ||||
| 	profileValidator *validator.Validate | ||||
| } | ||||
|  | ||||
| func (m *Manager) PersistProfile(profile *db.Profile) error { | ||||
| func (m *Manager) PersistProfile(ctx context.Context, profile *db.Profile) error { | ||||
| 	validationErrors := m.profileValidator.Struct(profile) | ||||
| 	if validationErrors != nil { | ||||
| 		return mapValidationErrorsToCommonError(validationErrors.(validator.ValidationErrors)) | ||||
| @@ -39,11 +39,11 @@ func (m *Manager) PersistProfile(profile *db.Profile) error { | ||||
| 		profile.SkinModel = "" | ||||
| 	} | ||||
|  | ||||
| 	return m.ProfilesRepository.SaveProfile(profile) | ||||
| 	return m.ProfilesRepository.SaveProfile(ctx, profile) | ||||
| } | ||||
|  | ||||
| func (m *Manager) RemoveProfileByUuid(uuid string) error { | ||||
| 	return m.ProfilesRepository.RemoveProfileByUuid(cleanupUuid(uuid)) | ||||
| func (m *Manager) RemoveProfileByUuid(ctx context.Context, uuid string) error { | ||||
| 	return m.ProfilesRepository.RemoveProfileByUuid(ctx, cleanupUuid(uuid)) | ||||
| } | ||||
|  | ||||
| type ValidationError struct { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package profiles | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| @@ -13,22 +14,12 @@ type ProfilesRepositoryMock struct { | ||||
| 	mock.Mock | ||||
| } | ||||
|  | ||||
| func (m *ProfilesRepositoryMock) FindProfileByUuid(uuid string) (*db.Profile, error) { | ||||
| 	args := m.Called(uuid) | ||||
| 	var result *db.Profile | ||||
| 	if casted, ok := args.Get(0).(*db.Profile); ok { | ||||
| 		result = casted | ||||
| 	} | ||||
|  | ||||
| 	return result, args.Error(1) | ||||
| func (m *ProfilesRepositoryMock) SaveProfile(ctx context.Context, profile *db.Profile) error { | ||||
| 	return m.Called(ctx, profile).Error(0) | ||||
| } | ||||
|  | ||||
| func (m *ProfilesRepositoryMock) SaveProfile(profile *db.Profile) error { | ||||
| 	return m.Called(profile).Error(0) | ||||
| } | ||||
|  | ||||
| func (m *ProfilesRepositoryMock) RemoveProfileByUuid(uuid string) error { | ||||
| 	return m.Called(uuid).Error(0) | ||||
| func (m *ProfilesRepositoryMock) RemoveProfileByUuid(ctx context.Context, uuid string) error { | ||||
| 	return m.Called(ctx, uuid).Error(0) | ||||
| } | ||||
|  | ||||
| type ManagerTestSuite struct { | ||||
| @@ -50,6 +41,7 @@ func (t *ManagerTestSuite) TearDownSubTest() { | ||||
|  | ||||
| func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 	t.Run("valid profile (full)", func() { | ||||
| 		ctx := context.Background() | ||||
| 		profile := &db.Profile{ | ||||
| 			Uuid:            "ba866a9c-c839-4268-a30f-7b26ae604c51", | ||||
| 			Username:        "mock-username", | ||||
| @@ -59,9 +51,9 @@ func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 			MojangTextures:  "eyJ0aW1lc3RhbXAiOjE0ODYzMzcyNTQ4NzIsInByb2ZpbGVJZCI6ImM0ZjFlNTZmNjFkMTQwYTc4YzMyOGQ5MTY2ZWVmOWU3IiwicHJvZmlsZU5hbWUiOiJXaHlZb3VSZWFkVGhpcyIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS83Mzk1NmE4ZTY0ZWU2ZDhlYzY1NmFkYmI0NDA0ZjhlYmZmMzQxMWIwY2I5MGIzMWNiNDc2ZWNiOTk2ZDNiOCJ9fX0=", | ||||
| 			MojangSignature: "QH+1rlQJYk8tW+8WlSJnzxZZUL5RIkeOO33dq84cgNoxwCkzL95Zy5pbPMFhoiMXXablqXeqyNRZDQa+OewgDBSZxm0BmkNmwdTLzCPHgnlNYhwbO4sirg3hKjCZ82ORZ2q7VP2NQIwNvc3befiCakhDlMWUuhjxe7p/HKNtmKA7a/JjzmzwW7BWMv8b88ZaQaMaAc7puFQcu2E54G2Zk2kyv3T1Bm7bV4m7ymbL8McOmQc6Ph7C95/EyqIK1a5gRBUHPEFIEj0I06YKTHsCRFU1U/hJpk98xXHzHuULJobpajqYXuVJ8QEVgF8k8dn9VkS8BMbXcjzfbb6JJ36v7YIV6Rlt75wwTk2wr3C3P0ij55y0iXth1HjwcEKsg54n83d9w8yQbkUCiTpMbOqxTEOOS7G2O0ZDBJDXAKQ4n5qCiCXKZ4febv4+dWVQtgfZHnpGJUD3KdduDKslMePnECOXMjGSAOQou//yze2EkL2rBpJtAAiOtvBlm/aWnDZpij5cQk+pWmeHWZIf0LSSlsYRUWRDk/VKBvUTEAO9fqOxWqmSgQRUY2Ea56u0ZsBb4vEa1UY6mlJj3+PNZaWu5aP2E9Unh0DIawV96eW8eFQgenlNXHMmXd4aOra4sz2eeOnY53JnJP+eVE4cB1hlq8RA2mnwTtcy3lahzZonOWc=", | ||||
| 		} | ||||
| 		t.ProfilesRepository.On("SaveProfile", profile).Once().Return(nil) | ||||
| 		t.ProfilesRepository.On("SaveProfile", ctx, profile).Once().Return(nil) | ||||
|  | ||||
| 		err := t.Manager.PersistProfile(profile) | ||||
| 		err := t.Manager.PersistProfile(ctx, profile) | ||||
| 		t.NoError(err) | ||||
| 	}) | ||||
|  | ||||
| @@ -70,9 +62,9 @@ func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 			Uuid:     "ba866a9c-c839-4268-a30f-7b26ae604c51", | ||||
| 			Username: "mock-username", | ||||
| 		} | ||||
| 		t.ProfilesRepository.On("SaveProfile", profile).Once().Return(nil) | ||||
| 		t.ProfilesRepository.On("SaveProfile", mock.Anything, profile).Once().Return(nil) | ||||
|  | ||||
| 		err := t.Manager.PersistProfile(profile) | ||||
| 		err := t.Manager.PersistProfile(context.Background(), profile) | ||||
| 		t.NoError(err) | ||||
| 	}) | ||||
|  | ||||
| @@ -86,9 +78,9 @@ func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 		expectedProfile := *profile | ||||
| 		expectedProfile.Uuid = "ba866a9cc8394268a30f7b26ae604c51" | ||||
| 		expectedProfile.SkinModel = "" | ||||
| 		t.ProfilesRepository.On("SaveProfile", &expectedProfile).Once().Return(nil) | ||||
| 		t.ProfilesRepository.On("SaveProfile", mock.Anything, &expectedProfile).Once().Return(nil) | ||||
|  | ||||
| 		err := t.Manager.PersistProfile(profile) | ||||
| 		err := t.Manager.PersistProfile(context.Background(), profile) | ||||
| 		t.NoError(err) | ||||
| 	}) | ||||
|  | ||||
| @@ -99,7 +91,7 @@ func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 			MojangTextures: "eyJ0aW1lc3RhbXAiOjE0ODYzMzcyNTQ4NzIsInByb2ZpbGVJZCI6ImM0ZjFlNTZmNjFkMTQwYTc4YzMyOGQ5MTY2ZWVmOWU3IiwicHJvZmlsZU5hbWUiOiJXaHlZb3VSZWFkVGhpcyIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS83Mzk1NmE4ZTY0ZWU2ZDhlYzY1NmFkYmI0NDA0ZjhlYmZmMzQxMWIwY2I5MGIzMWNiNDc2ZWNiOTk2ZDNiOCJ9fX0=", | ||||
| 		} | ||||
|  | ||||
| 		err := t.Manager.PersistProfile(profile) | ||||
| 		err := t.Manager.PersistProfile(context.Background(), profile) | ||||
| 		t.Error(err) | ||||
| 		t.IsType(&ValidationError{}, err) | ||||
| 		castedErr := err.(*ValidationError) | ||||
| @@ -114,7 +106,7 @@ func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 			Username: "invalid\"username", | ||||
| 		} | ||||
|  | ||||
| 		err := t.Manager.PersistProfile(profile) | ||||
| 		err := t.Manager.PersistProfile(context.Background(), profile) | ||||
| 		t.Error(err) | ||||
| 		t.IsType(&ValidationError{}, err) | ||||
| 		castedErr := err.(*ValidationError) | ||||
| @@ -126,7 +118,7 @@ func (t *ManagerTestSuite) TestPersistProfile() { | ||||
| 	t.Run("empty profile", func() { | ||||
| 		profile := &db.Profile{} | ||||
|  | ||||
| 		err := t.Manager.PersistProfile(profile) | ||||
| 		err := t.Manager.PersistProfile(context.Background(), profile) | ||||
| 		t.Error(err) | ||||
| 		t.IsType(&ValidationError{}, err) | ||||
| 		// TODO: validate errors | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| type ProfilesFinder interface { | ||||
| 	FindProfileByUsername(username string) (*db.Profile, error) | ||||
| 	FindProfileByUsername(ctx context.Context, username string) (*db.Profile, error) | ||||
| } | ||||
|  | ||||
| type MojangProfilesProvider interface { | ||||
| @@ -22,7 +22,7 @@ type Provider struct { | ||||
| } | ||||
|  | ||||
| func (p *Provider) FindProfileByUsername(ctx context.Context, username string, allowProxy bool) (*db.Profile, error) { | ||||
| 	profile, err := p.ProfilesFinder.FindProfileByUsername(username) | ||||
| 	profile, err := p.ProfilesFinder.FindProfileByUsername(ctx, username) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|   | ||||
| @@ -18,8 +18,8 @@ type ProfilesFinderMock struct { | ||||
| 	mock.Mock | ||||
| } | ||||
|  | ||||
| func (m *ProfilesFinderMock) FindProfileByUsername(username string) (*db.Profile, error) { | ||||
| 	args := m.Called(username) | ||||
| func (m *ProfilesFinderMock) FindProfileByUsername(ctx context.Context, username string) (*db.Profile, error) { | ||||
| 	args := m.Called(ctx, username) | ||||
| 	var result *db.Profile | ||||
| 	if casted, ok := args.Get(0).(*db.Profile); ok { | ||||
| 		result = casted | ||||
| @@ -67,14 +67,15 @@ func (t *CombinedProfilesProviderSuite) TearDownSubTest() { | ||||
|  | ||||
| func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 	t.Run("exists profile with a skin", func() { | ||||
| 		ctx := context.Background() | ||||
| 		profile := &db.Profile{ | ||||
| 			Uuid:     "mock-uuid", | ||||
| 			Username: "Mock", | ||||
| 			SkinUrl:  "https://example.com/skin.png", | ||||
| 		} | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", ctx, "Mock").Return(profile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(ctx, "Mock", true) | ||||
| 		t.NoError(err) | ||||
| 		t.Same(profile, foundProfile) | ||||
| 	}) | ||||
| @@ -85,7 +86,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 			Username: "Mock", | ||||
| 			CapeUrl:  "https://example.com/cape.png", | ||||
| 		} | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(profile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| 		t.NoError(err) | ||||
| @@ -97,7 +98,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 			Uuid:     "mock-uuid", | ||||
| 			Username: "Mock", | ||||
| 		} | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(profile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", false) | ||||
| 		t.NoError(err) | ||||
| @@ -105,7 +106,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("not exists profile (no proxy)", func() { | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", false) | ||||
| 		t.NoError(err) | ||||
| @@ -114,7 +115,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
|  | ||||
| 	t.Run("handle error from profiles repository", func() { | ||||
| 		expectedError := errors.New("mock error") | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, expectedError) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, expectedError) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", false) | ||||
| 		t.Same(expectedError, err) | ||||
| @@ -128,7 +129,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 		} | ||||
| 		mojangProfile := createMojangProfile(true, true) | ||||
| 		ctx := context.Background() | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", ctx, "Mock").Return(profile, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", ctx, "Mock").Return(mojangProfile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(ctx, "Mock", true) | ||||
| @@ -146,7 +147,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
|  | ||||
| 	t.Run("not exists profile (with proxy)", func() { | ||||
| 		mojangProfile := createMojangProfile(true, true) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(mojangProfile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| @@ -167,7 +168,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 			Uuid:     "mock-uuid", | ||||
| 			Username: "Mock", | ||||
| 		} | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(profile, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(profile, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(nil, errors.New("mock error")) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| @@ -176,7 +177,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("should not return an error when passed the invalid username", func() { | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(nil, mojang.InvalidUsername) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| @@ -186,7 +187,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
|  | ||||
| 	t.Run("should return an error from mojang provider", func() { | ||||
| 		expectedError := errors.New("mock error") | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(nil, expectedError) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| @@ -204,7 +205,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(mojangProfile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
| @@ -218,7 +219,7 @@ func (t *CombinedProfilesProviderSuite) TestFindByUsername() { | ||||
| 			Name:  "mOcK", | ||||
| 			Props: []*mojang.Property{}, | ||||
| 		} | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", "Mock").Return(nil, nil) | ||||
| 		t.ProfilesFinder.On("FindProfileByUsername", mock.Anything, "Mock").Return(nil, nil) | ||||
| 		t.MojangProfilesProvider.On("GetForUsername", mock.Anything, "Mock").Return(mojangProfile, nil) | ||||
|  | ||||
| 		foundProfile, err := t.Provider.FindProfileByUsername(context.Background(), "Mock", true) | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package security | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| @@ -18,7 +19,7 @@ type Signer struct { | ||||
| 	Key *rsa.PrivateKey | ||||
| } | ||||
|  | ||||
| func (s *Signer) SignTextures(textures string) (string, error) { | ||||
| func (s *Signer) SignTextures(ctx context.Context, textures string) (string, error) { | ||||
| 	message := []byte(textures) | ||||
| 	messageHash := sha1.New() | ||||
| 	_, err := messageHash.Write(message) | ||||
| @@ -35,6 +36,6 @@ func (s *Signer) SignTextures(textures string) (string, error) { | ||||
| 	return base64.StdEncoding.EncodeToString(signature), nil | ||||
| } | ||||
|  | ||||
| func (s *Signer) GetPublicKey() (*rsa.PublicKey, error) { | ||||
| func (s *Signer) GetPublicKey(ctx context.Context) (*rsa.PublicKey, error) { | ||||
| 	return &s.Key.PublicKey, nil | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package security | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/pem" | ||||
| @@ -24,7 +25,7 @@ func TestSigner_SignTextures(t *testing.T) { | ||||
|  | ||||
| 	signer := NewSigner(key) | ||||
|  | ||||
| 	signature, err := signer.SignTextures("eyJ0aW1lc3RhbXAiOjE2MTQzMDcxMzQsInByb2ZpbGVJZCI6ImZmYzhmZGM5NTgyNDUwOWU4YTU3Yzk5Yjk0MGZiOTk2IiwicHJvZmlsZU5hbWUiOiJFcmlja1NrcmF1Y2giLCJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9lbHkuYnkvc3RvcmFnZS9za2lucy82OWM2NzQwZDI5OTNlNWQ2ZjZhN2ZjOTI0MjBlZmMyOS5wbmcifX0sImVseSI6dHJ1ZX0") | ||||
| 	signature, err := signer.SignTextures(context.Background(), "eyJ0aW1lc3RhbXAiOjE2MTQzMDcxMzQsInByb2ZpbGVJZCI6ImZmYzhmZGM5NTgyNDUwOWU4YTU3Yzk5Yjk0MGZiOTk2IiwicHJvZmlsZU5hbWUiOiJFcmlja1NrcmF1Y2giLCJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly9lbHkuYnkvc3RvcmFnZS9za2lucy82OWM2NzQwZDI5OTNlNWQ2ZjZhN2ZjOTI0MjBlZmMyOS5wbmcifX0sImVseSI6dHJ1ZX0") | ||||
| 	require.NoError(t, err) | ||||
| 	require.Equal(t, "IyHCxTP5ITquEXTHcwCtLd08jWWy16JwlQeWg8naxhoAVQecHGRdzHRscuxtdq/446kmeox7h4EfRN2A2ZLL+A==", signature) | ||||
| } | ||||
| @@ -37,7 +38,7 @@ func TestSigner_GetPublicKey(t *testing.T) { | ||||
|  | ||||
| 	signer := NewSigner(key) | ||||
|  | ||||
| 	publicKey, err := signer.GetPublicKey() | ||||
| 	publicKey, err := signer.GetPublicKey(context.Background()) | ||||
| 	require.NoError(t, err) | ||||
| 	require.IsType(t, &rsa.PublicKey{}, publicKey) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user