diff --git a/api/user_http.go b/api/user_http.go index e12d131..d4de328 100644 --- a/api/user_http.go +++ b/api/user_http.go @@ -4,7 +4,7 @@ import ( "io/ioutil" "net/http" - "github.com/rinosukmandityo/hexagonal-login/logic" + "github.com/rinosukmandityo/hexagonal-login/helper" svc "github.com/rinosukmandityo/hexagonal-login/services" "github.com/go-chi/chi" @@ -30,7 +30,7 @@ func (u *userhandler) Get(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") user, e := u.userService.GetById(id) if e != nil { - if errors.Cause(e) == logic.ErrUserNotFound { + if errors.Cause(e) == helper.ErrUserNotFound { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } diff --git a/api/user_http_test.go b/api/user_http_test.go index a2a1cea..3025a53 100644 --- a/api/user_http_test.go +++ b/api/user_http_test.go @@ -15,10 +15,10 @@ import ( "testing" . "github.com/rinosukmandityo/hexagonal-login/api" - "github.com/rinosukmandityo/hexagonal-login/helper" "github.com/rinosukmandityo/hexagonal-login/logic" m "github.com/rinosukmandityo/hexagonal-login/models" repo "github.com/rinosukmandityo/hexagonal-login/repositories" + rh "github.com/rinosukmandityo/hexagonal-login/repositories/helper" "github.com/go-chi/chi" ) @@ -74,7 +74,7 @@ func UserTestData() []m.User { } func init() { - userRepo = helper.ChooseRepo() + userRepo = rh.ChooseRepo() userService := logic.NewUserService(userRepo) handler := NewUserHandler(userService) r = RegisterHandler(handler) @@ -150,6 +150,16 @@ func InsertUser(t *testing.T) { testdata := UserTestData() wg := sync.WaitGroup{} + // Clean test data if any + for _, data := range testdata { + wg.Add(1) + go func(_data m.User) { + userService.Delete(&_data) + wg.Done() + }(data) + } + wg.Wait() + t.Run("Case 1: Save data", func(t *testing.T) { for _, data := range testdata { wg.Add(1) diff --git a/helper/errmsg.go b/helper/errmsg.go new file mode 100644 index 0000000..09d9948 --- /dev/null +++ b/helper/errmsg.go @@ -0,0 +1,11 @@ +package helper + +import ( + "errors" +) + +var ( + ErrUserNotFound = errors.New("User Not Found") + ErrUserInvalid = errors.New("User Invalid") + ErrUserNameDuplicate = errors.New("User Name Already Exists") +) diff --git a/logic/login_logic.go b/logic/login_logic.go index 460ec84..542fec1 100644 --- a/logic/login_logic.go +++ b/logic/login_logic.go @@ -19,12 +19,3 @@ func NewLoginService(loginRepo repo.LoginRepository) svc.LoginService { func (u *loginService) Authenticate(username, password string) (bool, *m.User, error) { return u.loginRepo.Authenticate(username, password) } - -func (u *loginService) ChangePassword(user m.User, newpassword string) error { - return u.loginRepo.ChangePassword(user, newpassword) - -} -func (u *loginService) UserActivation(activationkey string) error { - return u.loginRepo.UserActivation(activationkey) - -} diff --git a/logic/user_logic.go b/logic/user_logic.go index ad365f5..2040bd2 100644 --- a/logic/user_logic.go +++ b/logic/user_logic.go @@ -1,8 +1,7 @@ package logic import ( - "errors" - + "github.com/rinosukmandityo/hexagonal-login/helper" m "github.com/rinosukmandityo/hexagonal-login/models" repo "github.com/rinosukmandityo/hexagonal-login/repositories" svc "github.com/rinosukmandityo/hexagonal-login/services" @@ -16,12 +15,6 @@ type userService struct { userRepo repo.UserRepository } -var ( - ErrUserNotFound = errors.New("User Not Found") - ErrUserInvalid = errors.New("User Invalid") - ErrUserNameDuplicate = errors.New("User Name Already Exists") -) - func NewUserService(userRepo repo.UserRepository) svc.UserService { return &userService{ userRepo, @@ -38,20 +31,20 @@ func (u *userService) GetById(id string) (*m.User, error) { } func (u *userService) Store(user *m.User) error { if e := validate.Validate(user); e != nil { - return errs.Wrap(ErrUserInvalid, "service.User.Store") + return errs.Wrap(helper.ErrUserInvalid, "service.User.Store") } if user.ID == "" { user.ID = shortid.MustGenerate() } if isFound, _, _ := u.userRepo.GetByUsername(user.Username); isFound { - return errs.Wrap(ErrUserNameDuplicate, "service.User.Store") + return errs.Wrap(helper.ErrUserNameDuplicate, "service.User.Store") } return u.userRepo.Store(user) } func (u *userService) Update(user *m.User) error { if e := validate.Validate(user); e != nil { - return errs.Wrap(ErrUserInvalid, "service.User.Update") + return errs.Wrap(helper.ErrUserInvalid, "service.User.Update") } if user.ID == "" { user.ID = shortid.MustGenerate() @@ -61,7 +54,7 @@ func (u *userService) Update(user *m.User) error { } func (u *userService) Delete(user *m.User) error { if user.ID == "" { - return errs.Wrap(ErrUserNotFound, "service.User.Delete") + return errs.Wrap(helper.ErrUserNotFound, "service.User.Delete") } if e := u.userRepo.Delete(user); e != nil { return e diff --git a/main.go b/main.go index 2e95990..9b4d1d5 100644 --- a/main.go +++ b/main.go @@ -9,8 +9,8 @@ import ( "syscall" h "github.com/rinosukmandityo/hexagonal-login/api" - "github.com/rinosukmandityo/hexagonal-login/helper" "github.com/rinosukmandityo/hexagonal-login/logic" + rh "github.com/rinosukmandityo/hexagonal-login/repositories/helper" ) /* @@ -24,7 +24,7 @@ import ( */ func main() { - userRepo := helper.ChooseRepo() + userRepo := rh.ChooseRepo() userService := logic.NewUserService(userRepo) handler := h.NewUserHandler(userService) diff --git a/helper/repo.go b/repositories/helper/repo.go similarity index 97% rename from helper/repo.go rename to repositories/helper/repo.go index 1de978b..27128c3 100644 --- a/helper/repo.go +++ b/repositories/helper/repo.go @@ -1,4 +1,4 @@ -package helper +package repohelper import ( "log" diff --git a/repositories/login_repository.go b/repositories/login_repository.go index ae56a8f..b0861a4 100644 --- a/repositories/login_repository.go +++ b/repositories/login_repository.go @@ -6,6 +6,4 @@ import ( type LoginRepository interface { Authenticate(username, password string) (bool, *m.User, error) - ChangePassword(user m.User, newpassword string) error - UserActivation(activationkey string) error } diff --git a/repositories/mongodb/login_mongo_repo.go b/repositories/mongodb/login_mongo_repo.go new file mode 100644 index 0000000..b1b77d9 --- /dev/null +++ b/repositories/mongodb/login_mongo_repo.go @@ -0,0 +1,76 @@ +package mongo + +import ( + "context" + "crypto/md5" + "fmt" + "io" + "time" + + "github.com/rinosukmandityo/hexagonal-login/helper" + m "github.com/rinosukmandityo/hexagonal-login/models" + repo "github.com/rinosukmandityo/hexagonal-login/repositories" + + "github.com/pkg/errors" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +type loginMongoRepository struct { + client *mongo.Client + database string + timeout time.Duration +} + +func newLoginMongoClient(mongoURL string, mongoTimeout int) (*mongo.Client, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(mongoTimeout)*time.Second) + defer cancel() + client, e := mongo.Connect(ctx, options.Client().ApplyURI(mongoURL)) + if e != nil { + return nil, e + } + if e = client.Ping(ctx, readpref.Primary()); e != nil { + return nil, e + } + return client, e +} + +func NewLoginMongoRepository(mongoURL, mongoDB string, mongoTimeout int) (repo.LoginRepository, error) { + repo := &loginMongoRepository{ + timeout: time.Duration(mongoTimeout) * time.Second, + database: mongoDB, + } + client, e := newLoginMongoClient(mongoURL, mongoTimeout) + if e != nil { + return nil, errors.Wrap(e, "repository.NewLoginMongoRepository") + } + repo.client = client + return repo, nil +} + +func (r *loginMongoRepository) Authenticate(username, password string) (bool, *m.User, error) { + ctx, cancel := context.WithTimeout(context.Background(), r.timeout) + defer cancel() + user := new(m.User) + c := r.client.Database(r.database).Collection(user.TableName()) + if e := c.FindOne(ctx, bson.M{"$or": []bson.M{ + {"Username": username}, + {"Email": username}, + }}).Decode(user); e != nil { + if e == mongo.ErrNoDocuments { + return false, nil, errors.Wrap(helper.ErrUserNotFound, "repository.Login.Authenticate") + } + return false, user, errors.Wrap(e, "repository.Login.Authenticate") + } + tPass := md5.New() + io.WriteString(tPass, password) + + ePassword := fmt.Sprintf("%x", tPass.Sum(nil)) + + if ePassword != user.Password { + return false, user, errors.Wrap(errors.New("Password is incorrect"), "repository.Login.Authenticate") + } + return true, user, nil +} diff --git a/repositories/mongodb/user_mongo_repo.go b/repositories/mongodb/user_mongo_repo.go index c686438..a84a0c8 100644 --- a/repositories/mongodb/user_mongo_repo.go +++ b/repositories/mongodb/user_mongo_repo.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/rinosukmandityo/hexagonal-login/logic" + "github.com/rinosukmandityo/hexagonal-login/helper" m "github.com/rinosukmandityo/hexagonal-login/models" repo "github.com/rinosukmandityo/hexagonal-login/repositories" @@ -68,7 +68,7 @@ func (r *userMongoRepository) GetById(id string) (*m.User, error) { c := r.client.Database(r.database).Collection(user.TableName()) if e := c.FindOne(ctx, bson.M{"_id": id}).Decode(user); e != nil { if e == mongo.ErrNoDocuments { - return nil, errors.Wrap(logic.ErrUserNotFound, "repository.User.GetById") + return nil, errors.Wrap(helper.ErrUserNotFound, "repository.User.GetById") } return user, errors.Wrap(e, "repository.User.GetById") } @@ -82,7 +82,7 @@ func (r *userMongoRepository) GetByUsername(username string) (bool, *m.User, err c := r.client.Database(r.database).Collection(user.TableName()) if e := c.FindOne(ctx, bson.M{"Username": username}).Decode(user); e != nil { if e == mongo.ErrNoDocuments { - return false, nil, errors.Wrap(logic.ErrUserNotFound, "repository.User.GetById") + return false, nil, errors.Wrap(helper.ErrUserNotFound, "repository.User.GetById") } return false, user, errors.Wrap(e, "repository.User.GetById") } @@ -119,7 +119,7 @@ func (r *userMongoRepository) Delete(user *m.User) error { ctx, cancel := context.WithTimeout(context.Background(), r.timeout) defer cancel() c := r.client.Database(r.database).Collection(new(m.User).TableName()) - if res, e := c.DeleteOne(ctx, user); e != nil { + if res, e := c.DeleteOne(ctx, bson.M{"_id": user.ID}); e != nil { return errors.Wrap(e, "repository.User.Delete") } else { if res.DeletedCount == 0 { diff --git a/repositories/redis/user_redis_repo.go b/repositories/redis/user_redis_repo.go index f16977a..24e7c39 100644 --- a/repositories/redis/user_redis_repo.go +++ b/repositories/redis/user_redis_repo.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/rinosukmandityo/hexagonal-login/logic" + "github.com/rinosukmandityo/hexagonal-login/helper" m "github.com/rinosukmandityo/hexagonal-login/models" repo "github.com/rinosukmandityo/hexagonal-login/repositories" @@ -59,7 +59,7 @@ func (r *userRedisRepository) GetById(id string) (*m.User, error) { return user, errors.Wrap(e, "repository.Redis.GetById") } if len(data) == 0 { - return nil, errors.Wrap(logic.ErrUserNotFound, "repository.User.GetById") + return nil, errors.Wrap(helper.ErrUserNotFound, "repository.User.GetById") } user.ID = data["ID"] user.Username = data["Username"] @@ -78,7 +78,7 @@ func (r *userRedisRepository) GetByUsername(username string) (bool, *m.User, err return false, user, errors.Wrap(e, "repository.Redis.GetById") } if len(data) == 0 { - return false, nil, errors.Wrap(logic.ErrUserNotFound, "repository.User.GetById") + return false, nil, errors.Wrap(helper.ErrUserNotFound, "repository.User.GetById") } user.ID = data["ID"] user.Username = data["Username"] diff --git a/services/login_service.go b/services/login_service.go index 96851c2..b623e28 100644 --- a/services/login_service.go +++ b/services/login_service.go @@ -6,6 +6,4 @@ import ( type LoginService interface { Authenticate(username, password string) (bool, *m.User, error) - ChangePassword(user m.User, newpassword string) error - UserActivation(activationkey string) error } diff --git a/services/user_service_test.go b/services/user_service_test.go index 41e632b..22a2a3e 100644 --- a/services/user_service_test.go +++ b/services/user_service_test.go @@ -6,9 +6,9 @@ import ( "sync" "testing" - "github.com/rinosukmandityo/hexagonal-login/helper" "github.com/rinosukmandityo/hexagonal-login/logic" m "github.com/rinosukmandityo/hexagonal-login/models" + rh "github.com/rinosukmandityo/hexagonal-login/repositories/helper" . "github.com/rinosukmandityo/hexagonal-login/services" ) @@ -61,7 +61,7 @@ func UserTestData() []m.User { } func init() { - userRepo := helper.ChooseRepo() + userRepo := rh.ChooseRepo() userService = logic.NewUserService(userRepo) } @@ -76,6 +76,16 @@ func InsertUser(t *testing.T) { testdata := UserTestData() wg := sync.WaitGroup{} + // Clean test data if any + for _, data := range testdata { + wg.Add(1) + go func(_data m.User) { + userService.Delete(&_data) + wg.Done() + }(data) + } + wg.Wait() + t.Run("Case 1: Save data", func(t *testing.T) { for _, data := range testdata { wg.Add(1)