diff --git a/action.go b/action.go index 9348a19..122ba1d 100644 --- a/action.go +++ b/action.go @@ -2,6 +2,7 @@ package usermngmt import ( "github.com/Selly-Modules/usermngmt/model" + "github.com/Selly-Modules/usermngmt/permission" "github.com/Selly-Modules/usermngmt/role" "github.com/Selly-Modules/usermngmt/user" ) @@ -42,6 +43,11 @@ func (s Service) ChangeAllUsersStatus(roleID, status string) error { return user.ChangeAllUsersStatus(roleID, status) } +// LoginWithEmailAndPassword ... +func (s Service) LoginWithEmailAndPassword(email, password string) (model.User, error) { + return user.LoginWithEmailAndPassword(email, password) +} + // // Role // @@ -62,3 +68,24 @@ func (s Service) UpdateRole(roleID string, payload model.RoleUpdateOptions) erro func (s Service) GetAllRoles(query model.RoleAllQuery) model.RoleAll { return role.All(query) } + +// +// Permission +// + +// permission methods + +// CreatePermission ... +func (s Service) CreatePermission(payload model.PermissionCreateOptions) error { + return permission.Create(payload) +} + +// UpdatePermission ... +func (s Service) UpdatePermission(permissionID string, payload model.PermissionUpdateOptions) error { + return permission.Update(permissionID, payload) +} + +// GetAllPermissions ... +func (s Service) GetAllPermissions(query model.PermissionAllQuery) model.PermissionAll { + return permission.All(query) +} diff --git a/database/db.go b/database/db.go index 62ba40d..c65a323 100644 --- a/database/db.go +++ b/database/db.go @@ -8,8 +8,9 @@ import ( // Table var ( - tableUser = "users" - tableRole = "roles" + tableUser = "users" + tableRole = "roles" + tablePermission = "permissions" ) var ( @@ -32,3 +33,8 @@ func GetUserCol() *mongo.Collection { func GetRoleCol() *mongo.Collection { return db.Collection(fmt.Sprintf("%s-%s", prefix, tableRole)) } + +// GetPermissionCol ... +func GetPermissionCol() *mongo.Collection { + return db.Collection(fmt.Sprintf("%s-%s", prefix, tablePermission)) +} diff --git a/model/db.go b/model/db.go index c6d00d7..b4a5132 100644 --- a/model/db.go +++ b/model/db.go @@ -30,3 +30,14 @@ type DBUser struct { CreatedAt time.Time `bson:"createdAt"` UpdatedAt time.Time `bson:"updatedAt"` } + +// DBPermission ... +type DBPermission struct { + ID primitive.ObjectID `bson:"_id"` + Name string `bson:"name"` + Code string `bson:"code"` + RoleID primitive.ObjectID `bson:"roleId"` + Desc string `bson:"desc"` + CreatedAt time.Time `bson:"createdAt"` + UpdatedAt time.Time `bson:"updatedAt"` +} diff --git a/model/permission_request.go b/model/permission_request.go new file mode 100644 index 0000000..b7ecc3f --- /dev/null +++ b/model/permission_request.go @@ -0,0 +1,90 @@ +package model + +import ( + "errors" + + "github.com/Selly-Modules/logger" + "github.com/Selly-Modules/mongodb" +) + +// PermissionCreateOptions ... +type PermissionCreateOptions struct { + Name string + RoleID string + Desc string +} + +// PermissionUpdateOptions ... +type PermissionUpdateOptions struct { + Name string + RoleID string + Desc string +} + +// PermissionAllQuery ... +type PermissionAllQuery struct { + Page int64 + Limit int64 +} + +// Validate ... +func (co PermissionCreateOptions) Validate() error { + // Name + if co.Name == "" { + logger.Error("usermngmt - Permission - Create: no name data", logger.LogData{ + "payload": co, + }) + return errors.New("no name data") + } + + // RoleID + if co.RoleID == "" { + logger.Error("usermngmt - Permission - Create: no roleID data", logger.LogData{ + "payload": co, + }) + return errors.New("no role id data") + } + if _, isValid := mongodb.NewIDFromString(co.RoleID); !isValid { + return errors.New("invalid role id data") + } + + // Desc + if co.Desc == "" { + logger.Error("usermngmt - Permission - Create: no desc data", logger.LogData{ + "payload": co, + }) + return errors.New("no desc data") + } + return nil +} + +// Validate ... +func (co PermissionUpdateOptions) Validate() error { + // Name + if co.Name == "" { + logger.Error("usermngmt - Permission - Update: no name data", logger.LogData{ + "payload": co, + }) + return errors.New("no name data") + } + + // RoleID + if co.RoleID == "" { + logger.Error("usermngmt - Permission - Update: no roleID data", logger.LogData{ + "payload": co, + }) + return errors.New("no role id data") + } + if _, isValid := mongodb.NewIDFromString(co.RoleID); !isValid { + return errors.New("invalid role id data") + } + + // Desc + if co.Desc == "" { + logger.Error("usermngmt - Permission - Update: no desc data", logger.LogData{ + "payload": co, + }) + return errors.New("no desc data") + } + return nil +} diff --git a/model/permission_response.go b/model/permission_response.go new file mode 100644 index 0000000..9557aa2 --- /dev/null +++ b/model/permission_response.go @@ -0,0 +1,24 @@ +package model + +import ( + "time" +) + +// Permission ... +type Permission struct { + ID string `json:"_id"` + Name string `json:"name"` + Code string `json:"code"` + RoleID string `json:"roleId"` + Desc string `json:"desc"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type ( + // PermissionAll ... + PermissionAll struct { + List []Permission `json:"list"` + Total int64 `json:"total"` + } +) diff --git a/permission/db.go b/permission/db.go new file mode 100644 index 0000000..ff4e951 --- /dev/null +++ b/permission/db.go @@ -0,0 +1,97 @@ +package permission + +import ( + "context" + "fmt" + + "github.com/Selly-Modules/logger" + "github.com/Selly-Modules/usermngmt/database" + "github.com/Selly-Modules/usermngmt/model" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func findByID(ctx context.Context, id primitive.ObjectID) (model.DBPermission, error) { + var ( + doc model.DBPermission + col = database.GetPermissionCol() + ) + err := col.FindOne(ctx, bson.M{"_id": id}).Decode(&doc) + return doc, err +} + +func create(ctx context.Context, doc model.DBPermission) error { + var ( + col = database.GetPermissionCol() + ) + _, err := col.InsertOne(ctx, doc) + if err != nil { + logger.Error("usermngmt - Permission - InsertOne", logger.LogData{ + "doc": doc, + "err": err.Error(), + }) + return fmt.Errorf("error when create permission: %s", err.Error()) + } + + return nil +} + +func updateOneByCondition(ctx context.Context, cond interface{}, payload interface{}) error { + var ( + col = database.GetPermissionCol() + ) + _, err := col.UpdateOne(ctx, cond, payload) + if err != nil { + logger.Error("usermngmt - Permission - UpdateOne", logger.LogData{ + "cond": cond, + "payload": payload, + "err": err.Error(), + }) + return fmt.Errorf("error when update permission: %s", err.Error()) + } + + return err +} + +func findByCondition(ctx context.Context, cond interface{}, opts ...*options.FindOptions) (docs []model.DBPermission) { + var ( + col = database.GetPermissionCol() + ) + docs = make([]model.DBPermission, 0) + + cursor, err := col.Find(ctx, cond, opts...) + if err != nil { + logger.Error("usermngmt - Permission - Find", logger.LogData{ + "cond": cond, + "opts": opts, + "err": err.Error(), + }) + return + } + defer cursor.Close(ctx) + if err = cursor.All(ctx, &docs); err != nil { + logger.Error("usermngmt - Permission - Decode", logger.LogData{ + "cond": cond, + "opts": opts, + "err": err.Error(), + }) + return + } + return +} + +// countByCondition ... +func countByCondition(ctx context.Context, cond interface{}) int64 { + var ( + col = database.GetPermissionCol() + ) + total, err := col.CountDocuments(ctx, cond) + if err != nil { + logger.Error("usermngmt - Permission - CountDocuments", logger.LogData{ + "err": err.Error(), + "cond": cond, + }) + } + return total +} diff --git a/permission/handle.go b/permission/handle.go new file mode 100644 index 0000000..1a67eb2 --- /dev/null +++ b/permission/handle.go @@ -0,0 +1,142 @@ +package permission + +import ( + "context" + "errors" + "sync" + + "github.com/Selly-Modules/mongodb" + "github.com/Selly-Modules/usermngmt/internal" + "github.com/Selly-Modules/usermngmt/model" + "go.mongodb.org/mongo-driver/bson" +) + +// Create ... +func Create(payload model.PermissionCreateOptions) error { + var ( + ctx = context.Background() + ) + + // Validate payload + if err := payload.Validate(); err != nil { + return err + } + + // New permission data from payload + doc := newPermission(payload) + + // Create permission + if err := create(ctx, doc); err != nil { + return err + } + + return nil +} + +// newPermission ... +func newPermission(payload model.PermissionCreateOptions) model.DBPermission { + timeNow := internal.Now() + roleID, _ := mongodb.NewIDFromString(payload.RoleID) + return model.DBPermission{ + ID: mongodb.NewObjectID(), + Name: payload.Name, + Code: internal.GenerateCode(payload.Name), + RoleID: roleID, + Desc: payload.Desc, + CreatedAt: timeNow, + UpdatedAt: timeNow, + } +} + +// Update ... +func Update(permissionID string, payload model.PermissionUpdateOptions) error { + var ( + ctx = context.Background() + ) + + // Validate payload + if err := payload.Validate(); err != nil { + return err + } + + // Validate permissionID + id, isValid := mongodb.NewIDFromString(permissionID) + if !isValid { + return errors.New("invalid permission id data") + } + + // Setup condition + cond := bson.M{ + "_id": id, + } + + // Setup update data + roleID, _ := mongodb.NewIDFromString(payload.RoleID) + updateData := bson.M{ + "$set": bson.M{ + "name": payload.Name, + "code": internal.GenerateCode(payload.Name), + "roleId": roleID, + "desc": payload.Desc, + "updatedAt": internal.Now(), + }, + } + + // Update + if err := updateOneByCondition(ctx, cond, updateData); err != nil { + return err + } + + return nil +} + +// All ... +func All(queryParams model.PermissionAllQuery) (r model.PermissionAll) { + var ( + ctx = context.Background() + wg sync.WaitGroup + cond = bson.M{} + ) + query := model.CommonQuery{ + Page: queryParams.Page, + Limit: queryParams.Limit, + Sort: bson.M{"createdAt": -1}, + } + + // Assign condition + query.SetDefaultLimit() + + wg.Add(1) + go func() { + defer wg.Done() + docs := findByCondition(ctx, cond, query.GetFindOptionsUsingPage()) + r.List = getResponseList(docs) + }() + + wg.Add(1) + go func() { + defer wg.Done() + r.Total = countByCondition(ctx, cond) + }() + + wg.Wait() + + return +} + +func getResponseList(permissions []model.DBPermission) []model.Permission { + res := make([]model.Permission, 0) + for _, permission := range permissions { + res = append(res, model.Permission{ + ID: permission.ID.Hex(), + Name: permission.Name, + Code: permission.Code, + RoleID: permission.RoleID.Hex(), + Desc: permission.Desc, + CreatedAt: permission.CreatedAt, + UpdatedAt: permission.UpdatedAt, + }) + } + + return res +} diff --git a/user/db.go b/user/db.go index b55209b..d6f9aa4 100644 --- a/user/db.go +++ b/user/db.go @@ -166,3 +166,12 @@ func countByCondition(ctx context.Context, cond interface{}) int64 { } return total } + +func findOneByCondition(ctx context.Context, cond interface{}) (model.DBUser, error) { + var ( + col = database.GetUserCol() + doc model.DBUser + ) + err := col.FindOne(ctx, cond).Decode(&doc) + return doc, err +} diff --git a/user/handle.go b/user/handle.go index 7cf8f2d..fc5831c 100644 --- a/user/handle.go +++ b/user/handle.go @@ -96,7 +96,11 @@ func All(queryParams model.UserAllQuery) (r model.UserAll) { go func() { defer wg.Done() docs := findByCondition(ctx, cond, query.GetFindOptionsUsingPage()) - r.List = getResponseList(ctx, docs) + res := make([]model.User, 0) + for _, doc := range docs { + res = append(res, getResponse(ctx, doc)) + } + r.List = res }() wg.Add(1) @@ -110,29 +114,23 @@ func All(queryParams model.UserAllQuery) (r model.UserAll) { return } -func getResponseList(ctx context.Context, users []model.DBUser) []model.User { - res := make([]model.User, 0) - - for _, user := range users { - roleRaw, _ := roleFindByID(ctx, user.RoleID) - res = append(res, model.User{ - ID: user.ID.Hex(), - Name: user.Name, - Phone: user.Phone, - Email: user.Email, - Status: user.Status, - Role: model.RoleShort{ - ID: roleRaw.ID.Hex(), - Name: roleRaw.Name, - IsAdmin: roleRaw.IsAdmin, - }, - Other: user.Other, - CreatedAt: user.CreatedAt, - UpdatedAt: user.UpdatedAt, - }) +func getResponse(ctx context.Context, user model.DBUser) model.User { + roleRaw, _ := roleFindByID(ctx, user.RoleID) + return model.User{ + ID: user.ID.Hex(), + Name: user.Name, + Phone: user.Phone, + Email: user.Email, + Status: user.Status, + Role: model.RoleShort{ + ID: roleRaw.ID.Hex(), + Name: roleRaw.Name, + IsAdmin: roleRaw.IsAdmin, + }, + Other: user.Other, + CreatedAt: user.CreatedAt, + UpdatedAt: user.UpdatedAt, } - - return res } // UpdateByUserID ... @@ -290,3 +288,34 @@ func ChangeAllUsersStatus(roleID, status string) error { return nil } + +// LoginWithEmailAndPassword ... +func LoginWithEmailAndPassword(email, password string) (result model.User, err error) { + var ( + ctx = context.Background() + ) + + // Validate email, password + if email == "" || password == "" { + err = errors.New("email or password cannot be empty") + return + } + + // Find user + user, _ := findOneByCondition(ctx, bson.M{ + "email": email, + }) + if user.ID.IsZero() { + err = errors.New("user not found") + return + } + + // Check Password + if !internal.CheckPasswordHash(password, user.HashedPassword) { + err = errors.New("the password is incorrect") + return + } + + result = getResponse(ctx, user) + return +}