feat: add a new REST endpoint - GET /api/v1/user
This commit is contained in:
parent
ffcb965590
commit
5d07fba5b7
|
@ -2,6 +2,7 @@ package bundb
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
|
@ -36,6 +37,26 @@ func (a *APIKey) Create(ctx context.Context, params domain.CreateAPIKeyParams) (
|
|||
return apiKey.ToDomain(), nil
|
||||
}
|
||||
|
||||
func (a *APIKey) Get(ctx context.Context, key string) (domain.APIKey, error) {
|
||||
var ak model.APIKey
|
||||
|
||||
if err := a.db.NewSelect().
|
||||
Model(&ak).
|
||||
Where("key = ?", key).
|
||||
Scan(ctx); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.APIKey{}, domain.APIKeyNotFoundError{Key: key}
|
||||
}
|
||||
return domain.APIKey{}, fmt.Errorf(
|
||||
"something went wrong while selecting api key (key=%s) from the db: %w",
|
||||
key,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return ak.ToDomain(), nil
|
||||
}
|
||||
|
||||
func mapCreateAPIKeyError(err error, params domain.CreateAPIKeyParams) error {
|
||||
var pgError pgdriver.Error
|
||||
if !errors.As(err, &pgError) {
|
||||
|
|
|
@ -33,7 +33,6 @@ func TestAPIKey_Create(t *testing.T) {
|
|||
assert.Equal(t, params.Key(), apiKey.Key)
|
||||
assert.Equal(t, params.UserID(), apiKey.UserID)
|
||||
assert.WithinDuration(t, time.Now(), apiKey.CreatedAt, time.Second)
|
||||
assert.Zero(t, apiKey.LastUsedAt)
|
||||
})
|
||||
|
||||
t.Run("ERR: player doesn't exist", func(t *testing.T) {
|
||||
|
@ -66,3 +65,30 @@ func TestAPIKey_Create(t *testing.T) {
|
|||
assert.Zero(t, apiKey)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIKey_Get(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db := newDB(t)
|
||||
fixture := loadFixtures(t, db)
|
||||
repo := bundb.NewAPIKey(db)
|
||||
apiKeyFromFixture := fixture.apiKey(t, "user-1-api-key-1")
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ak, err := repo.Get(context.Background(), apiKeyFromFixture.Key)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, apiKeyFromFixture, ak)
|
||||
})
|
||||
|
||||
t.Run("ERR: API key not found", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ak, err := repo.Get(context.Background(), apiKeyFromFixture.Key+"1")
|
||||
assert.ErrorIs(t, err, domain.APIKeyNotFoundError{
|
||||
Key: apiKeyFromFixture.Key + "1",
|
||||
})
|
||||
assert.Zero(t, ak)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,11 +10,10 @@ import (
|
|||
type APIKey struct {
|
||||
bun.BaseModel `bun:"base_model,table:api_keys,alias:ak"`
|
||||
|
||||
ID int64 `bun:"id,pk,autoincrement,identity"`
|
||||
Key string `bun:"key,nullzero,type:varchar(255),notnull,unique"`
|
||||
UserID int64 `bun:"user_id,nullzero,notnull"`
|
||||
CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"`
|
||||
LastUsedAt time.Time `bun:"last_used_at,nullzero"`
|
||||
ID int64 `bun:"id,pk,autoincrement,identity"`
|
||||
Key string `bun:"key,nullzero,type:varchar(255),notnull,unique"`
|
||||
UserID int64 `bun:"user_id,nullzero,notnull"`
|
||||
CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"`
|
||||
}
|
||||
|
||||
func NewAPIKey(p domain.CreateAPIKeyParams) APIKey {
|
||||
|
@ -27,10 +26,9 @@ func NewAPIKey(p domain.CreateAPIKeyParams) APIKey {
|
|||
|
||||
func (a *APIKey) ToDomain() domain.APIKey {
|
||||
return domain.APIKey{
|
||||
ID: a.ID,
|
||||
Key: a.Key,
|
||||
UserID: a.UserID,
|
||||
CreatedAt: a.CreatedAt,
|
||||
LastUsedAt: a.LastUsedAt,
|
||||
ID: a.ID,
|
||||
Key: a.Key,
|
||||
UserID: a.UserID,
|
||||
CreatedAt: a.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,18 +14,15 @@ func TestAPIKey(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
var id int64 = 123
|
||||
lastUsedAt := time.Now().Add(-5 * time.Minute)
|
||||
params, err := domain.NewCreateAPIKeyParams(uuid.NewString(), 1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := model.NewAPIKey(params)
|
||||
result.ID = id
|
||||
result.LastUsedAt = lastUsedAt
|
||||
|
||||
apiKey := result.ToDomain()
|
||||
assert.Equal(t, id, apiKey.ID)
|
||||
assert.Equal(t, params.Key(), apiKey.Key)
|
||||
assert.Equal(t, params.UserID(), apiKey.UserID)
|
||||
assert.WithinDuration(t, time.Now(), apiKey.CreatedAt, 10*time.Millisecond)
|
||||
assert.Equal(t, lastUsedAt, apiKey.LastUsedAt)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -9,11 +10,10 @@ const (
|
|||
)
|
||||
|
||||
type APIKey struct {
|
||||
ID int64
|
||||
Key string
|
||||
UserID int64
|
||||
CreatedAt time.Time
|
||||
LastUsedAt time.Time
|
||||
ID int64
|
||||
Key string
|
||||
UserID int64
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateAPIKeyParams struct {
|
||||
|
@ -48,3 +48,19 @@ func (c CreateAPIKeyParams) Key() string {
|
|||
func (c CreateAPIKeyParams) UserID() int64 {
|
||||
return c.userID
|
||||
}
|
||||
|
||||
type APIKeyNotFoundError struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
func (e APIKeyNotFoundError) Error() string {
|
||||
return fmt.Sprintf("API key (key=%s) not found", e.Key)
|
||||
}
|
||||
|
||||
func (e APIKeyNotFoundError) UserError() string {
|
||||
return e.Error()
|
||||
}
|
||||
|
||||
func (e APIKeyNotFoundError) Code() ErrorCode {
|
||||
return ErrorCodeEntityNotFound
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
|
||||
|
@ -62,3 +63,15 @@ func TestNewCreateAPIKeyParams(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIKeyNotFoundError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := domain.APIKeyNotFoundError{
|
||||
Key: uuid.NewString(),
|
||||
}
|
||||
var _ domain.Error = err
|
||||
assert.Equal(t, fmt.Sprintf("API key (key=%s) not found", err.Key), err.Error())
|
||||
assert.Equal(t, err.Error(), err.UserError())
|
||||
assert.Equal(t, domain.ErrorCodeEntityNotFound, err.Code())
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
//counterfeiter:generate -o internal/mock/api_key_repository.gen.go . APIKeyRepository
|
||||
type APIKeyRepository interface {
|
||||
Create(ctx context.Context, params domain.CreateAPIKeyParams) (domain.APIKey, error)
|
||||
Get(ctx context.Context, key string) (domain.APIKey, error)
|
||||
}
|
||||
|
||||
//counterfeiter:generate -o internal/mock/user_getter.gen.go . UserGetter
|
||||
|
@ -54,3 +55,24 @@ func (a *APIKey) Create(ctx context.Context, userID int64) (domain.APIKey, error
|
|||
|
||||
return apiKey, nil
|
||||
}
|
||||
|
||||
func (a *APIKey) Verify(ctx context.Context, keyStr string) (domain.User, error) {
|
||||
key, err := uuid.Parse(keyStr)
|
||||
if err != nil {
|
||||
return domain.User{}, domain.APIKeyNotFoundError{
|
||||
Key: keyStr,
|
||||
}
|
||||
}
|
||||
|
||||
ak, err := a.repo.Get(ctx, key.String())
|
||||
if err != nil {
|
||||
return domain.User{}, fmt.Errorf("APIKeyRepository.Get: %w", err)
|
||||
}
|
||||
|
||||
user, err := a.userSvc.Get(ctx, ak.UserID)
|
||||
if err != nil {
|
||||
return domain.User{}, fmt.Errorf("UserService.Get: %w", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
|
|
@ -53,3 +53,45 @@ func TestAPIKey_Create(t *testing.T) {
|
|||
assert.Zero(t, ak)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIKey_Verify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
apiKeyRepo := &mock.FakeAPIKeyRepository{}
|
||||
apiKeyRepo.GetCalls(func(ctx context.Context, key string) (domain.APIKey, error) {
|
||||
return domain.APIKey{
|
||||
ID: 1,
|
||||
Key: key,
|
||||
UserID: 125,
|
||||
CreatedAt: time.Now(),
|
||||
}, nil
|
||||
})
|
||||
|
||||
userSvc := &mock.FakeUserGetter{}
|
||||
userSvc.GetCalls(func(ctx context.Context, id int64) (domain.User, error) {
|
||||
return domain.User{
|
||||
ID: id,
|
||||
Name: uuid.NewString(),
|
||||
CreatedAt: time.Now(),
|
||||
}, nil
|
||||
})
|
||||
|
||||
user, err := service.NewAPIKey(apiKeyRepo, userSvc).Verify(context.Background(), uuid.NewString())
|
||||
assert.NoError(t, err)
|
||||
assert.Greater(t, user.ID, int64(0))
|
||||
})
|
||||
|
||||
t.Run("ERR: invalid uuid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := "abcd"
|
||||
user, err := service.NewAPIKey(nil, nil).Verify(context.Background(), key)
|
||||
assert.ErrorIs(t, err, domain.APIKeyNotFoundError{
|
||||
Key: key,
|
||||
})
|
||||
assert.Zero(t, user)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue