Add role management module with controller, service, repository, DTOs, and routes

This commit is contained in:
Habib Fatkhul Rohman 2025-10-21 15:04:58 +07:00
parent f28b3a5244
commit 4bd48c3bca
6 changed files with 647 additions and 0 deletions

View File

@ -0,0 +1,129 @@
package controller
import (
"net/http"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/service"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/Caknoooo/go-pagination"
"github.com/gin-gonic/gin"
"github.com/samber/do"
"gorm.io/gorm"
)
type (
RoleController interface {
CreateRole(ctx *gin.Context)
GetRoles(ctx *gin.Context)
GetRoleByID(ctx *gin.Context)
UpdateRole(ctx *gin.Context)
DeleteRole(ctx *gin.Context)
AssignPermissionsToRole(ctx *gin.Context)
RemovePermissionsFromRole(ctx *gin.Context)
AssignRoleToUser(ctx *gin.Context)
RemoveRoleFromUser(ctx *gin.Context)
GetRolesByUserID(ctx *gin.Context)
}
roleController struct {
roleService service.RoleService
db *gorm.DB
}
)
// AssignPermissionsToRole implements RoleController.
func (r *roleController) AssignPermissionsToRole(ctx *gin.Context) {
panic("unimplemented")
}
// AssignRoleToUser implements RoleController.
func (r *roleController) AssignRoleToUser(ctx *gin.Context) {
panic("unimplemented")
}
// CreateRole implements RoleController.
func (r *roleController) CreateRole(ctx *gin.Context) {
var role dto.RoleCreateRequest
if err := ctx.ShouldBind(&role); err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil)
ctx.AbortWithStatusJSON(http.StatusBadRequest, res)
return
}
result, err := r.roleService.CreateRole(ctx.Request.Context(), role)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_REGISTER_ROLE, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
res := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_REGISTER_ROLE, result)
ctx.JSON(http.StatusOK, res)
}
// DeleteRole implements RoleController.
func (r *roleController) DeleteRole(ctx *gin.Context) {
panic("unimplemented")
}
// GetRoleByID implements RoleController.
func (r *roleController) GetRoleByID(ctx *gin.Context) {
panic("unimplemented")
}
// GetRoles implements RoleController.
func (r *roleController) GetRoles(ctx *gin.Context) {
clientId := ctx.MustGet("client_id").(string)
// logrus.Info("Client ID: ", clientId)
var filter = &query.RoleFilter{
ClientID: clientId,
Name: ctx.Query("name"),
}
// logrus.Info("Filter: ", filter)
filter.BindPagination(ctx)
ctx.ShouldBindQuery(filter)
roles, total, err := pagination.PaginatedQueryWithIncludable[query.M_Role](r.db, filter)
if err != nil {
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_ROLE, err.Error(), nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
paginationResponse := pagination.CalculatePagination(filter.Pagination, total)
response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_ROLE, roles, paginationResponse)
ctx.JSON(http.StatusOK, response)
}
// GetRolesByUserID implements RoleController.
func (r *roleController) GetRolesByUserID(ctx *gin.Context) {
panic("unimplemented")
}
// RemovePermissionsFromRole implements RoleController.
func (r *roleController) RemovePermissionsFromRole(ctx *gin.Context) {
panic("unimplemented")
}
// RemoveRoleFromUser implements RoleController.
func (r *roleController) RemoveRoleFromUser(ctx *gin.Context) {
panic("unimplemented")
}
// UpdateRole implements RoleController.
func (r *roleController) UpdateRole(ctx *gin.Context) {
panic("unimplemented")
}
func NewRoleController(injector *do.Injector, rs service.RoleService) RoleController {
db := do.MustInvokeNamed[*gorm.DB](injector, constants.DB)
return &roleController{
roleService: rs,
db: db,
}
}

View File

@ -0,0 +1,75 @@
package dto
import "errors"
const (
// Failed
MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body"
MESSAGE_FAILED_REGISTER_ROLE = "failed create user"
MESSAGE_FAILED_GET_LIST_ROLE = "failed get list user"
MESSAGE_FAILED_TOKEN_NOT_VALID = "token not valid"
MESSAGE_FAILED_TOKEN_NOT_FOUND = "token not found"
MESSAGE_FAILED_GET_ROLE = "failed get role"
MESSAGE_FAILED_LOGIN = "failed login"
MESSAGE_FAILED_UPDATE_ROLE = "failed update role"
MESSAGE_FAILED_DELETE_ROLE = "failed delete role"
MESSAGE_FAILED_PROSES_REQUEST = "failed proses request"
MESSAGE_FAILED_DENIED_ACCESS = "denied access"
MESSAGE_FAILED_VERIFY_EMAIL = "failed verify email"
// Success
MESSAGE_SUCCESS_REGISTER_ROLE = "success create role"
MESSAGE_SUCCESS_GET_LIST_ROLE = "success get list role"
MESSAGE_SUCCESS_GET_ROLE = "success get role"
MESSAGE_SUCCESS_LOGIN = "success login"
MESSAGE_SUCCESS_UPDATE_ROLE = "success update role"
MESSAGE_SUCCESS_DELETE_ROLE = "success delete role"
MESSAGE_SEND_VERIFICATION_EMAIL_SUCCESS = "success send verification email"
MESSAGE_SUCCESS_VERIFY_EMAIL = "success verify email"
)
var (
ErrCreateRole = errors.New("failed to create role")
ErrGetRoleById = errors.New("failed to get role by id")
ErrGetRoleByName = errors.New("failed to get role by name")
ErrRoleAlreadyExists = errors.New("role already exist")
ErrUpdateRole = errors.New("failed to update role")
ErrRoleNotFound = errors.New("role not found")
ErrDeleteRole = errors.New("failed to delete role")
)
type RoleCreateRequest struct {
Name string `json:"name" binding:"required"`
Description string `json:"description" binding:"omitempty"`
IconUrl string `json:"icon_url" binding:"omitempty"`
Type string `json:"type" binding:"omitempty"`
HomeUrl string `json:"home_url" binding:"omitempty"`
ClientID string `json:"client_id" binding:"required,uuid4"`
RoleMenus []string `json:"role_menus" binding:"omitempty"`
RolePermissions []string `json:"role_permissions" binding:"omitempty"`
UserRoles []string `json:"user_roles" binding:"omitempty"`
Users []string `json:"users" binding:"omitempty"`
Permissions []string `json:"permissions" binding:"omitempty"`
}
type RoleUpdateRequest struct {
Name string `json:"name" binding:"omitempty"`
Description string `json:"description" binding:"omitempty"`
IconUrl string `json:"icon_url" binding:"omitempty"`
Type string `json:"type" binding:"omitempty"`
HomeUrl string `json:"home_url" binding:"omitempty"`
ClientID string `json:"client_id" binding:"omitempty,uuid4"`
Permissions []string `json:"permissions" binding:"omitempty"`
}
type RoleResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
IconUrl string `json:"icon_url"`
Type string `json:"type"`
HomeUrl string `json:"home_url"`
ClientID string `json:"client_id"`
Permissions []string `json:"permissions"`
}

View File

@ -0,0 +1,66 @@
package query
import (
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/Caknoooo/go-pagination"
"gorm.io/gorm"
)
type M_Role struct {
ID string `json:"id"`
Name string `json:"name"`
Permissions []entities.M_Permissions `json:"permissions" gorm:"many2many:m_role_permissions;joinForeignKey:RoleID;JoinReferences:PermissionID"`
ClientID string `json:"client_id"`
}
type RoleFilter struct {
pagination.BaseFilter
Name string `form:"name"` // tambahkan ini
ClientID string `form:"client_id"` // tambahkan ini
}
func (f *RoleFilter) ApplyFilters(query *gorm.DB) *gorm.DB {
// Apply your filters here
if f.Name != "" {
query = query.Where("name ILIKE ?", "%"+f.Name+"%")
}
if f.ClientID != "" {
query = query.Where("client_id = ?", f.ClientID)
}
return query
}
func (f *RoleFilter) GetTableName() string {
return "m_roles"
}
func (f *RoleFilter) GetSearchFields() []string {
return []string{"name"}
}
func (f *RoleFilter) GetDefaultSort() string {
return "id asc"
}
func (f *RoleFilter) GetIncludes() []string {
return f.Includes
}
func (f *RoleFilter) GetPagination() pagination.PaginationRequest {
return f.Pagination
}
func (f *RoleFilter) Validate() {
var validIncludes []string
allowedIncludes := f.GetAllowedIncludes()
for _, include := range f.Includes {
if allowedIncludes[include] {
validIncludes = append(validIncludes, include)
}
}
f.Includes = validIncludes
}
func (f *RoleFilter) GetAllowedIncludes() map[string]bool {
return map[string]bool{}
}

View File

@ -0,0 +1,207 @@
package repository
import (
"context"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/google/uuid"
"gorm.io/gorm"
)
// UserRepository interface {
// Register(ctx context.Context, tx *gorm.DB, user entities.M_User) (entities.M_User, error)
// GetUserById(ctx context.Context, tx *gorm.DB, userId string) (entities.M_User, error)
// GetUserByEmail(ctx context.Context, tx *gorm.DB, email string) (entities.M_User, error)
// CheckEmail(ctx context.Context, tx *gorm.DB, email string) (entities.M_User, bool, error)
// Update(ctx context.Context, tx *gorm.DB, user entities.M_User) (entities.M_User, error)
// Delete(ctx context.Context, tx *gorm.DB, userId string) error
// SwitchRole(ctx context.Context, tx *gorm.DB, userId string, roleId string) (entities.M_User, error)
// }
type RoleRepository interface {
CreateRole(ctx context.Context, tx *gorm.DB, role entities.M_Role) (entities.M_Role, error)
GetRoleByID(ctx context.Context, tx *gorm.DB, id string) (entities.M_Role, error)
UpdateRole(ctx context.Context, tx *gorm.DB, id string, role entities.M_Role) (entities.M_Role, error)
DeleteRole(ctx context.Context, tx *gorm.DB, id string) error
AssignPermissionsToRole(ctx context.Context, tx *gorm.DB, roleId string, permissions []string) error
RemovePermissionsFromRole(ctx context.Context, tx *gorm.DB, roleId string, permissions []string) error
AssignRoleToUser(ctx context.Context, tx *gorm.DB, userId string, roleId string) error
RemoveRoleFromUser(ctx context.Context, tx *gorm.DB, userId string, roleId string) error
GetRolesByUserID(ctx context.Context, tx *gorm.DB, userId string) ([]entities.M_Role, error)
CheckRoleName(ctx context.Context, tx *gorm.DB, name string) (entities.M_Role, bool, error)
}
type roleRepository struct {
db *gorm.DB
}
// CheckRoleName implements RoleRepository.
func (r *roleRepository) CheckRoleName(ctx context.Context, tx *gorm.DB, name string) (entities.M_Role, bool, error) {
if tx == nil {
tx = r.db
}
var role entities.M_Role
if err := tx.WithContext(ctx).Where("name = ?", name).First(&role).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return entities.M_Role{}, false, nil
}
return entities.M_Role{}, false, err
}
return role, true, nil
}
// AssignPermissionsToRole implements RoleRepository.
func (r *roleRepository) AssignPermissionsToRole(ctx context.Context, tx *gorm.DB, roleId string, permissions []string) error {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Model(&entities.M_Role{}).Where("id = ?", roleId).Update("permissions", gorm.Expr("array_cat(permissions, ?)", permissions)).Error; err != nil {
return err
}
return nil
}
func (r *roleRepository) AssignRoleToUser(ctx context.Context, tx *gorm.DB, userId string, roleId string) error {
if tx == nil {
tx = r.db
}
userUUID, err := uuid.Parse(userId)
if err != nil {
return err
}
roleUUID, err := uuid.Parse(roleId)
if err != nil {
return err
}
var count int64
if err := tx.WithContext(ctx).
Model(&entities.M_User_Role{}).
Where("user_id = ? AND role_id = ?", userUUID, roleUUID).
Count(&count).Error; err != nil {
return err
}
if count > 0 {
return nil
}
userRole := entities.M_User_Role{
UserID: userUUID,
RoleID: roleUUID,
}
// Insert ke tabel user_roles, bukan update ke M_User
if err := tx.WithContext(ctx).Create(&userRole).Error; err != nil {
return err
}
return nil
}
// CreateRole implements RoleRepository.
func (r *roleRepository) CreateRole(ctx context.Context, tx *gorm.DB, role entities.M_Role) (entities.M_Role, error) {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Create(&role).Error; err != nil {
return entities.M_Role{}, err
}
return role, nil
}
// DeleteRole implements RoleRepository.
func (r *roleRepository) DeleteRole(ctx context.Context, tx *gorm.DB, id string) error {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Where("id = ?", id).Delete(&entities.M_Role{}).Error; err != nil {
return err
}
return nil
}
// GetRoleByID implements RoleRepository.
func (r *roleRepository) GetRoleByID(ctx context.Context, tx *gorm.DB, id string) (entities.M_Role, error) {
if tx == nil {
tx = r.db
}
var role entities.M_Role
if err := tx.WithContext(ctx).Where("id = ?", id).First(&role).Error; err != nil {
return entities.M_Role{}, err
}
return role, nil
}
// GetRolesByUserID implements RoleRepository.
func (r *roleRepository) GetRolesByUserID(ctx context.Context, tx *gorm.DB, userId string) ([]entities.M_Role, error) {
if tx == nil {
tx = r.db
}
var roles []entities.M_Role
if err := tx.WithContext(ctx).Where("user_id = ?", userId).Find(&roles).Error; err != nil {
return nil, err
}
return roles, nil
}
// RemovePermissionsFromRole implements RoleRepository.
func (r *roleRepository) RemovePermissionsFromRole(ctx context.Context, tx *gorm.DB, roleId string, permissions []string) error {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Model(&entities.M_Role{}).Where("id = ?", roleId).Update("permissions", gorm.Expr("array_remove(permissions, ?)", permissions)).Error; err != nil {
return err
}
return nil
}
// RemoveRoleFromUser implements RoleRepository.
func (r *roleRepository) RemoveRoleFromUser(ctx context.Context, tx *gorm.DB, userId string, roleId string) error {
if tx == nil {
tx = r.db
}
userUUID, err := uuid.Parse(userId)
if err != nil {
return err
}
roleUUID, err := uuid.Parse(roleId)
if err != nil {
return err
}
var count int64
if err := tx.WithContext(ctx).
Model(&entities.M_User_Role{}).
Where("user_id = ? AND role_id = ?", userUUID, roleUUID).
Count(&count).Error; err != nil {
return err
}
if count > 0 {
return nil
}
userRole := entities.M_User_Role{
UserID: userUUID,
RoleID: roleUUID,
}
// Delete ke tabel user_roles, bukan update ke M_User
if err := tx.WithContext(ctx).Delete(&userRole).Error; err != nil {
return err
}
return nil
}
// UpdateRole implements RoleRepository.
func (r *roleRepository) UpdateRole(ctx context.Context, tx *gorm.DB, id string, role entities.M_Role) (entities.M_Role, error) {
if tx == nil {
tx = r.db
}
if err := tx.WithContext(ctx).Where("id = ?", id).Updates(&role).Error; err != nil {
return entities.M_Role{}, err
}
return role, nil
}
func NewRoleRepository(db *gorm.DB) RoleRepository {
return &roleRepository{
db: db,
}
}

39
modules/role/routes.go Normal file
View File

@ -0,0 +1,39 @@
package role
import (
"github.com/Caknoooo/go-gin-clean-starter/middlewares"
"github.com/Caknoooo/go-gin-clean-starter/modules/auth/service"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/controller"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
"github.com/gin-gonic/gin"
"github.com/samber/do"
)
func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
roleController := do.MustInvoke[controller.RoleController](injector)
jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService)
roleRoutes := server.Group("/api/v1/role")
{
roleRoutes.POST("", middlewares.Authenticate(jwtService), roleController.CreateRole)
roleRoutes.GET("", middlewares.Authenticate(jwtService), roleController.GetRoles)
roleRoutes.GET("/:id", middlewares.Authenticate(jwtService), roleController.GetRoleByID)
roleRoutes.PUT("/:id", middlewares.Authenticate(jwtService), roleController.UpdateRole)
roleRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), roleController.DeleteRole)
roleRoutes.POST("/:id/permissions", middlewares.Authenticate(jwtService), roleController.AssignPermissionsToRole)
roleRoutes.DELETE("/:id/permissions", middlewares.Authenticate(jwtService), roleController.RemovePermissionsFromRole)
roleRoutes.POST("/assign-role", middlewares.Authenticate(jwtService), roleController.AssignRoleToUser)
roleRoutes.POST("/remove-role", middlewares.Authenticate(jwtService), roleController.RemoveRoleFromUser)
roleRoutes.GET("/user/:id", middlewares.Authenticate(jwtService), roleController.GetRolesByUserID)
// roleRoutes.POST("", roleController.CreateRole)
// roleRoutes.GET("", roleController.GetRoles)
// roleRoutes.GET("/:id", roleController.GetRoleByID)
// roleRoutes.PUT("/:id", middlewares.Authenticate(jwtService), roleController.Update)
// roleRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), roleController.Delete)
// userRoutes.PUT("/:id", middlewares.Authenticate(jwtService), userController.Update)
// roleRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), roleController.Delete)
// roleRoutes.POST("/send-verification-email", roleController.SendVerificationEmail)
// roleRoutes.POST("/refresh", middlewares.Authenticate(jwtService), roleController.Refresh)
}
}

View File

@ -0,0 +1,131 @@
package service
import (
"context"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
authRepo "github.com/Caknoooo/go-gin-clean-starter/modules/auth/repository"
"github.com/Caknoooo/go-gin-clean-starter/modules/auth/service"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/dto"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/query"
"github.com/Caknoooo/go-gin-clean-starter/modules/role/repository"
"github.com/google/uuid"
"gorm.io/gorm"
)
type RoleService interface {
CreateRole(ctx context.Context, role dto.RoleCreateRequest) (dto.RoleResponse, error)
GetRoles(ctx context.Context, filter query.RoleFilter) ([]dto.RoleResponse, error)
GetRoleByID(ctx context.Context, id string) (dto.RoleResponse, error)
UpdateRole(ctx context.Context, id string, role dto.RoleUpdateRequest) (dto.RoleResponse, error)
DeleteRole(ctx context.Context, id string) error
AssignPermissionsToRole(ctx context.Context, roleId string, permissions []string) error
RemovePermissionsFromRole(ctx context.Context, roleId string, permissions []string) error
AssignRoleToUser(ctx context.Context, userId string, roleId string) error
RemoveRoleFromUser(ctx context.Context, userId string, roleId string) error
GetRolesByUserID(ctx context.Context, userId string) ([]dto.RoleResponse, error)
}
type roleService struct {
roleRepo repository.RoleRepository
refreshTokenRepository authRepo.RefreshTokenRepository
jwtService service.JWTService
db *gorm.DB
}
// AssignPermissionsToRole implements RoleService.
func (r *roleService) AssignPermissionsToRole(ctx context.Context, roleId string, permissions []string) error {
panic("unimplemented")
}
// AssignRoleToUser implements RoleService.
func (r *roleService) AssignRoleToUser(ctx context.Context, userId string, roleId string) error {
panic("unimplemented")
}
// CreateRole implements RoleService.
func (r *roleService) CreateRole(ctx context.Context, req dto.RoleCreateRequest) (dto.RoleResponse, error) {
_, exists, err := r.roleRepo.CheckRoleName(ctx, r.db, req.Name)
if err != nil && err != gorm.ErrRecordNotFound {
return dto.RoleResponse{}, err
}
if exists {
return dto.RoleResponse{}, dto.ErrRoleAlreadyExists
}
clientUUID, err := uuid.Parse(req.ClientID)
if err != nil {
return dto.RoleResponse{}, err
}
role := entities.M_Role{
Name: req.Name,
Description: req.Description,
IconUrl: req.IconUrl,
Type: req.Type,
HomeUrl: req.HomeUrl,
ClientID: clientUUID,
}
createdRole, err := r.roleRepo.CreateRole(ctx, r.db, role)
if err != nil {
return dto.RoleResponse{}, err
}
return dto.RoleResponse{
ID: createdRole.ID.String(),
Name: createdRole.Name,
Description: createdRole.Description,
IconUrl: createdRole.IconUrl,
Type: createdRole.Type,
HomeUrl: createdRole.HomeUrl,
ClientID: createdRole.ClientID.String(),
}, nil
}
// DeleteRole implements RoleService.
func (r *roleService) DeleteRole(ctx context.Context, id string) error {
panic("unimplemented")
}
// GetRoleByID implements RoleService.
func (r *roleService) GetRoleByID(ctx context.Context, id string) (dto.RoleResponse, error) {
panic("unimplemented")
}
// GetRoles implements RoleService.
func (r *roleService) GetRoles(ctx context.Context, filter query.RoleFilter) ([]dto.RoleResponse, error) {
panic("unimplemented")
}
// GetRolesByUserID implements RoleService.
func (r *roleService) GetRolesByUserID(ctx context.Context, userId string) ([]dto.RoleResponse, error) {
panic("unimplemented")
}
// RemovePermissionsFromRole implements RoleService.
func (r *roleService) RemovePermissionsFromRole(ctx context.Context, roleId string, permissions []string) error {
panic("unimplemented")
}
// RemoveRoleFromUser implements RoleService.
func (r *roleService) RemoveRoleFromUser(ctx context.Context, userId string, roleId string) error {
panic("unimplemented")
}
// UpdateRole implements RoleService.
func (r *roleService) UpdateRole(ctx context.Context, id string, role dto.RoleUpdateRequest) (dto.RoleResponse, error) {
panic("unimplemented")
}
func NewRoleService(
roleRepo repository.RoleRepository,
refreshTokenRepo authRepo.RefreshTokenRepository,
jwtService service.JWTService,
db *gorm.DB,
) RoleService {
return &roleService{
roleRepo: roleRepo,
refreshTokenRepository: refreshTokenRepo,
jwtService: jwtService,
db: db,
}
}