Skip to content

Commit

Permalink
Go, Go Context! Added context transfer literally everywhere
Browse files Browse the repository at this point in the history
  • Loading branch information
erickskrauch committed Feb 13, 2024
1 parent fdafbc4 commit f5bc474
Show file tree
Hide file tree
Showing 21 changed files with 210 additions and 247 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/jellydator/ttlcache/v3 v3.1.1
github.com/mediocregopher/radix/v4 v4.1.4
github.com/mono83/slf v0.0.0-20170919161409-79153e9636db
github.com/SentimensRG/ctx v0.0.0-20180729130232-0bfd988c655d
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.1
github.com/valyala/fastjson v1.6.4
Expand Down
47 changes: 10 additions & 37 deletions internal/db/redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const userUuidToUsernameKey = "hash:uuid-to-username"

type Redis struct {
client radix.Client
context context.Context
serializer db.ProfileSerializer
}

Expand All @@ -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)

Expand All @@ -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)
}))
}
Expand Down Expand Up @@ -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)
}))
}
Expand Down Expand Up @@ -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)

Expand All @@ -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)
}))
}
Expand Down
62 changes: 22 additions & 40 deletions internal/db/redis/redis_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand All @@ -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",
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -235,24 +213,26 @@ 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")
s.Require().Empty(uuidResp)
})

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)
Expand All @@ -261,31 +241,33 @@ 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)
})
}

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")
s.Require().Equal(resp, "MoCk:d3ca513eb3e14946b58047f2bd3530fd")
})

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")
Expand Down
9 changes: 5 additions & 4 deletions internal/http/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package http

import (
"context"
"errors"
"fmt"
"net/http"
Expand All @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down
19 changes: 10 additions & 9 deletions internal/http/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package http

import (
"bytes"
"context"
"errors"
"io"
"net/http"
Expand All @@ -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 {
Expand All @@ -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",
Expand Down Expand Up @@ -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"},
},
Expand All @@ -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")
Expand All @@ -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()
Expand All @@ -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()
Expand Down
Loading

0 comments on commit f5bc474

Please sign in to comment.