Skip to content

Commit

Permalink
introduce user's dashboard MVP (being able to manage their comments a…
Browse files Browse the repository at this point in the history
…nd bookmarks) (#24)
  • Loading branch information
khanzadimahdi committed Aug 31, 2024
1 parent 140a352 commit a9e1851
Show file tree
Hide file tree
Showing 86 changed files with 3,723 additions and 196 deletions.
61 changes: 53 additions & 8 deletions backend/application/auth/verify/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,34 @@ import (

"github.com/khanzadimahdi/testproject/application/auth"
"github.com/khanzadimahdi/testproject/domain"
"github.com/khanzadimahdi/testproject/domain/config"
"github.com/khanzadimahdi/testproject/domain/password"
"github.com/khanzadimahdi/testproject/domain/role"
"github.com/khanzadimahdi/testproject/domain/user"
"github.com/khanzadimahdi/testproject/infrastructure/jwt"
)

type UseCase struct {
userRepository user.Repository
hasher password.Hasher
jwt *jwt.JWT
userRepository user.Repository
roleRepository role.Repository
configRepository config.Repository
hasher password.Hasher
jwt *jwt.JWT
}

func NewUseCase(userRepository user.Repository, hasher password.Hasher, JWT *jwt.JWT) *UseCase {
func NewUseCase(
userRepository user.Repository,
roleRepository role.Repository,
configRepository config.Repository,
hasher password.Hasher,
JWT *jwt.JWT,
) *UseCase {
return &UseCase{
userRepository: userRepository,
hasher: hasher,
jwt: JWT,
userRepository: userRepository,
roleRepository: roleRepository,
configRepository: configRepository,
hasher: hasher,
jwt: JWT,
}
}

Expand Down Expand Up @@ -99,13 +111,46 @@ func (uc *UseCase) Execute(request Request) (*Response, error) {
},
}

if _, err := uc.userRepository.Save(&u); err != nil {
userUUID, err := uc.userRepository.Save(&u)
if err != nil {
return nil, err
}

if err := uc.assignDefaultRoles(userUUID); err != nil {
return nil, err
}

return &Response{}, nil
}

func (uc *UseCase) assignDefaultRoles(userUUID string) error {
c, err := uc.configRepository.GetLatestRevision()
if errors.Is(err, domain.ErrNotExists) {
return nil
} else if err != nil {
return err
}

roles, err := uc.roleRepository.GetByUUIDs(c.UserDefaultRoleUUIDs)
if err != nil {
return err
}

for i := range roles {
userUUIDs := make([]string, len(roles[i].UserUUIDs)+1)
copy(userUUIDs, roles[i].UserUUIDs)
userUUIDs[len(userUUIDs)-1] = userUUID

roles[i].UserUUIDs = userUUIDs

if _, err := uc.roleRepository.Save(&roles[i]); err != nil {
return err
}
}

return nil
}

func (uc *UseCase) identityExists(identity string) (bool, error) {
u, err := uc.userRepository.GetOneByIdentity(identity)
if errors.Is(err, domain.ErrNotExists) {
Expand Down
99 changes: 81 additions & 18 deletions backend/application/auth/verify/usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import (

"github.com/khanzadimahdi/testproject/application/auth"
"github.com/khanzadimahdi/testproject/domain"
"github.com/khanzadimahdi/testproject/domain/config"
"github.com/khanzadimahdi/testproject/domain/role"
"github.com/khanzadimahdi/testproject/domain/user"
"github.com/khanzadimahdi/testproject/infrastructure/crypto/ecdsa"
crypto "github.com/khanzadimahdi/testproject/infrastructure/crypto/mock"
"github.com/khanzadimahdi/testproject/infrastructure/jwt"
configRepo "github.com/khanzadimahdi/testproject/infrastructure/repository/mocks/config"
"github.com/khanzadimahdi/testproject/infrastructure/repository/mocks/roles"
"github.com/khanzadimahdi/testproject/infrastructure/repository/mocks/users"
)

Expand All @@ -28,8 +32,10 @@ func TestUseCase_Execute(t *testing.T) {

t.Run("verifies user registration", func(t *testing.T) {
var (
userRepository users.MockUsersRepository
hasher crypto.MockCrypto
userRepository users.MockUsersRepository
roleRepository roles.MockRolesRepository
configRepository configRepo.MockConfigRepository
hasher crypto.MockCrypto

u = user.User{
UUID: "user-uuid",
Expand All @@ -42,6 +48,29 @@ func TestUseCase_Execute(t *testing.T) {
Password: "test-password",
Repassword: "test-password",
}

roles = []role.Role{
{UUID: "role-uuid-1", Name: "role-1", UserUUIDs: []string{"user-uuid-1", "user-uuid-2"}},
{UUID: "role-uuid-2", Name: "role-2", UserUUIDs: []string{"user-uuid-1", "user-uuid-2"}},
}

c = config.Config{
Revision: 2,
UserDefaultRoleUUIDs: []string{roles[0].UUID, roles[1].UUID},
}

expectedRoles = []role.Role{
{
UUID: "role-uuid-1",
Name: "role-1",
UserUUIDs: []string{"user-uuid-1", "user-uuid-2", u.UUID},
},
{
UUID: "role-uuid-2",
Name: "role-2",
UserUUIDs: []string{"user-uuid-1", "user-uuid-2", u.UUID},
},
}
)

userRepository.On("GetOneByIdentity", u.UUID).Once().Return(user.User{}, domain.ErrNotExists)
Expand All @@ -52,7 +81,15 @@ func TestUseCase_Execute(t *testing.T) {
hasher.On("Hash", []byte(r.Password), mock.AnythingOfType("[]uint8")).Once().Return([]byte("hashed-password"), nil)
defer hasher.AssertExpectations(t)

response, err := NewUseCase(&userRepository, &hasher, j).Execute(r)
configRepository.On("GetLatestRevision").Once().Return(c, nil)
defer configRepository.AssertExpectations(t)

roleRepository.On("GetByUUIDs", c.UserDefaultRoleUUIDs).Once().Return(roles, nil)
roleRepository.On("Save", &expectedRoles[0]).Once().Return(expectedRoles[0].UUID, nil)
roleRepository.On("Save", &expectedRoles[1]).Once().Return(expectedRoles[1].UUID, nil)
defer roleRepository.AssertExpectations(t)

response, err := NewUseCase(&userRepository, &roleRepository, &configRepository, &hasher, j).Execute(r)

assert.NoError(t, err)
assert.NotNil(t, response)
Expand All @@ -61,8 +98,10 @@ func TestUseCase_Execute(t *testing.T) {

t.Run("validation fails", func(t *testing.T) {
var (
userRepository users.MockUsersRepository
hasher crypto.MockCrypto
userRepository users.MockUsersRepository
roleRepository roles.MockRolesRepository
configRepository configRepo.MockConfigRepository
hasher crypto.MockCrypto

r = Request{}

Expand All @@ -77,11 +116,14 @@ func TestUseCase_Execute(t *testing.T) {
}
)

response, err := NewUseCase(&userRepository, &hasher, j).Execute(r)
response, err := NewUseCase(&userRepository, &roleRepository, &configRepository, &hasher, j).Execute(r)

userRepository.AssertNotCalled(t, "GetOneByIdentity")
userRepository.AssertNotCalled(t, "Save")
hasher.AssertNotCalled(t, "Hash")
configRepository.AssertNotCalled(t, "GetLatestRevision")
roleRepository.AssertNotCalled(t, "GetByUUIDs")
roleRepository.AssertNotCalled(t, "Save")

assert.NoError(t, err)
assert.NotNil(t, response)
Expand All @@ -90,8 +132,10 @@ func TestUseCase_Execute(t *testing.T) {

t.Run("invalid token", func(t *testing.T) {
var (
userRepository users.MockUsersRepository
hasher crypto.MockCrypto
userRepository users.MockUsersRepository
roleRepository roles.MockRolesRepository
configRepository configRepo.MockConfigRepository
hasher crypto.MockCrypto

u = user.User{
UUID: "user-uuid",
Expand All @@ -112,11 +156,14 @@ func TestUseCase_Execute(t *testing.T) {
}
)

response, err := NewUseCase(&userRepository, &hasher, j).Execute(r)
response, err := NewUseCase(&userRepository, &roleRepository, &configRepository, &hasher, j).Execute(r)

userRepository.AssertNotCalled(t, "GetOneByIdentity")
userRepository.AssertNotCalled(t, "Save")
hasher.AssertNotCalled(t, "Hash")
configRepository.AssertNotCalled(t, "GetLatestRevision")
roleRepository.AssertNotCalled(t, "GetByUUIDs")
roleRepository.AssertNotCalled(t, "Save")

assert.NoError(t, err)
assert.NotNil(t, response)
Expand All @@ -125,8 +172,10 @@ func TestUseCase_Execute(t *testing.T) {

t.Run("user with same identity exists", func(t *testing.T) {
var (
userRepository users.MockUsersRepository
hasher crypto.MockCrypto
userRepository users.MockUsersRepository
roleRepository roles.MockRolesRepository
configRepository configRepo.MockConfigRepository
hasher crypto.MockCrypto

u = user.User{
UUID: "user-uuid",
Expand All @@ -151,10 +200,13 @@ func TestUseCase_Execute(t *testing.T) {
userRepository.On("GetOneByIdentity", u.UUID).Once().Return(u, nil)
defer userRepository.AssertExpectations(t)

response, err := NewUseCase(&userRepository, &hasher, j).Execute(r)
response, err := NewUseCase(&userRepository, &roleRepository, &configRepository, &hasher, j).Execute(r)

userRepository.AssertNotCalled(t, "Save")
hasher.AssertNotCalled(t, "Hash")
configRepository.AssertNotCalled(t, "GetLatestRevision")
roleRepository.AssertNotCalled(t, "GetByUUIDs")
roleRepository.AssertNotCalled(t, "Save")

assert.NoError(t, err)
assert.NotNil(t, response)
Expand All @@ -163,8 +215,10 @@ func TestUseCase_Execute(t *testing.T) {

t.Run("user with same username exists", func(t *testing.T) {
var (
userRepository users.MockUsersRepository
hasher crypto.MockCrypto
userRepository users.MockUsersRepository
roleRepository roles.MockRolesRepository
configRepository configRepo.MockConfigRepository
hasher crypto.MockCrypto

u = user.User{
UUID: "user-uuid",
Expand All @@ -190,10 +244,13 @@ func TestUseCase_Execute(t *testing.T) {
userRepository.On("GetOneByIdentity", r.Username).Once().Return(u, nil)
defer userRepository.AssertExpectations(t)

response, err := NewUseCase(&userRepository, &hasher, j).Execute(r)
response, err := NewUseCase(&userRepository, &roleRepository, &configRepository, &hasher, j).Execute(r)

userRepository.AssertNotCalled(t, "Save")
hasher.AssertNotCalled(t, "Hash")
configRepository.AssertNotCalled(t, "GetLatestRevision")
roleRepository.AssertNotCalled(t, "GetByUUIDs")
roleRepository.AssertNotCalled(t, "Save")

assert.NoError(t, err)
assert.NotNil(t, response)
Expand All @@ -202,8 +259,10 @@ func TestUseCase_Execute(t *testing.T) {

t.Run("saving user's data failed", func(t *testing.T) {
var (
userRepository users.MockUsersRepository
hasher crypto.MockCrypto
userRepository users.MockUsersRepository
roleRepository roles.MockRolesRepository
configRepository configRepo.MockConfigRepository
hasher crypto.MockCrypto

u = user.User{
UUID: "user-uuid",
Expand All @@ -229,7 +288,11 @@ func TestUseCase_Execute(t *testing.T) {
hasher.On("Hash", []byte(r.Password), mock.AnythingOfType("[]uint8")).Once().Return([]byte("hashed-password"), nil)
defer hasher.AssertExpectations(t)

response, err := NewUseCase(&userRepository, &hasher, j).Execute(r)
response, err := NewUseCase(&userRepository, &roleRepository, &configRepository, &hasher, j).Execute(r)

configRepository.AssertNotCalled(t, "GetLatestRevision")
roleRepository.AssertNotCalled(t, "GetByUUIDs")
roleRepository.AssertNotCalled(t, "Save")

assert.ErrorIs(t, err, expectedErr)
assert.Nil(t, response)
Expand Down
29 changes: 29 additions & 0 deletions backend/application/bookmark/bookmarkExists/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package bookmarkExists

import "github.com/khanzadimahdi/testproject/domain/bookmark"

type validationErrors map[string]string

type Request struct {
ObjectType string `json:"object_type"`
ObjectUUID string `json:"object_uuid"`
OwnerUUID string `json:"-"`
}

func (r *Request) Validate() (bool, validationErrors) {
errors := make(validationErrors)

if r.ObjectType != bookmark.ObjectTypeArticle {
errors["object_type"] = "object type is not supported"
}

if len(r.ObjectUUID) == 0 {
errors["object_uuid"] = "object uuid is required"
}

if len(r.OwnerUUID) == 0 {
errors["owner_uuid"] = "owner uuid is required"
}

return len(errors) == 0, errors
}
7 changes: 7 additions & 0 deletions backend/application/bookmark/bookmarkExists/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package bookmarkExists

type Response struct {
ValidationErrors validationErrors `json:"errors,omitempty"`

Exist bool `json:"exist"`
}
40 changes: 40 additions & 0 deletions backend/application/bookmark/bookmarkExists/usecase.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package bookmarkExists

import (
"errors"

"github.com/khanzadimahdi/testproject/domain"
"github.com/khanzadimahdi/testproject/domain/bookmark"
)

type UseCase struct {
bookmarkRepository bookmark.Repository
}

func NewUseCase(
bookmarkRepository bookmark.Repository,
) *UseCase {
return &UseCase{
bookmarkRepository: bookmarkRepository,
}
}

func (uc *UseCase) Execute(request *Request) (*Response, error) {
if ok, validation := request.Validate(); !ok {
return &Response{
ValidationErrors: validation,
}, nil
}

if _, err := uc.bookmarkRepository.GetByOwnerUUID(request.OwnerUUID, request.ObjectType, request.ObjectUUID); errors.Is(err, domain.ErrNotExists) {
return &Response{
Exist: false,
}, nil
} else if err != nil {
return nil, err
}

return &Response{
Exist: true,
}, nil
}
Loading

0 comments on commit a9e1851

Please sign in to comment.