feat(role): Refactor role handling to include client details in responses and update API routes

This commit is contained in:
Habib Fatkhul Rohman 2025-10-31 11:27:29 +07:00
parent 14cbd567b3
commit 82579efe36
5 changed files with 136 additions and 71 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/Caknoooo/go-gin-clean-starter/modules/role/query" "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/modules/role/service"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants" "github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils" "github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
"github.com/Caknoooo/go-pagination" "github.com/Caknoooo/go-pagination"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -45,7 +46,7 @@ type (
// @Param body body dto.AssignPermissionRequest true "Assign permissions payload" // @Param body body dto.AssignPermissionRequest true "Assign permissions payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
// @Router /roles/{id}/assign-permissions [post] // @Router /roles/{id}/permissions [post]
func (r *roleController) AssignPermissionsToRole(ctx *gin.Context) { func (r *roleController) AssignPermissionsToRole(ctx *gin.Context) {
var req dto.AssignPermissionRequest var req dto.AssignPermissionRequest
roleId := ctx.Param("id") roleId := ctx.Param("id")
@ -189,7 +190,6 @@ func (r *roleController) GetRoles(ctx *gin.Context) {
} }
// logrus.Info("Filter: ", filter) // logrus.Info("Filter: ", filter)
filter.BindPagination(ctx) filter.BindPagination(ctx)
ctx.ShouldBindQuery(filter) ctx.ShouldBindQuery(filter)
roles, total, err := pagination.PaginatedQueryWithIncludable[query.M_Role](r.db, filter) roles, total, err := pagination.PaginatedQueryWithIncludable[query.M_Role](r.db, filter)
@ -198,9 +198,14 @@ func (r *roleController) GetRoles(ctx *gin.Context) {
ctx.JSON(http.StatusBadRequest, res) ctx.JSON(http.StatusBadRequest, res)
return return
} }
// Di GetRoles:
var roleResponses []dto.RoleResponse
for _, role := range roles {
roleResponses = append(roleResponses, mapRoleToResponse(role))
}
paginationResponse := pagination.CalculatePagination(filter.Pagination, total) paginationResponse := pagination.CalculatePagination(filter.Pagination, total)
response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_ROLE, roles, paginationResponse) response := pagination.NewPaginatedResponse(http.StatusOK, dto.MESSAGE_SUCCESS_GET_LIST_ROLE, roleResponses, paginationResponse)
ctx.JSON(http.StatusOK, response) ctx.JSON(http.StatusOK, response)
} }
@ -238,7 +243,7 @@ func (r *roleController) GetRolesByUserID(ctx *gin.Context) {
// @Param body body dto.RemovePermissionRequest true "Remove permissions payload" // @Param body body dto.RemovePermissionRequest true "Remove permissions payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
// @Router /roles/{id}/remove-permissions [post] // @Router /roles/{id}/permissions [post]
func (r *roleController) RemovePermissionsFromRole(ctx *gin.Context) { func (r *roleController) RemovePermissionsFromRole(ctx *gin.Context) {
var req dto.RemovePermissionRequest var req dto.RemovePermissionRequest
roleId := ctx.Param("id") roleId := ctx.Param("id")
@ -323,3 +328,18 @@ func NewRoleController(injector *do.Injector, rs service.RoleService) RoleContro
db: db, db: db,
} }
} }
func mapRoleToResponse(role query.M_Role) dto.RoleResponse {
return dto.RoleResponse{
ID: role.ID,
Name: role.Name,
Description: role.Description,
IconUrl: role.IconUrl,
Type: role.Type,
HomeUrl: role.HomeUrl,
Client: pkgdto.IdNameResponse{
ID: role.Client.ID.String(),
Name: role.Client.Name,
},
}
}

View File

@ -1,6 +1,10 @@
package dto package dto
import "errors" import (
"errors"
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
)
const ( const (
// Failed // Failed
@ -72,13 +76,14 @@ type RoleUpdateRequest struct {
} }
type RoleResponse struct { type RoleResponse struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
IconUrl string `json:"icon_url"` IconUrl string `json:"icon_url"`
Type string `json:"type"` Type string `json:"type"`
HomeUrl string `json:"home_url"` HomeUrl string `json:"home_url"`
ClientID string `json:"client_id"` // ClientID string `json:"client_id"`
Client pkgdto.IdNameResponse `json:"client"`
Permissions []RolePermissionsResponse `json:"permissions,omitempty"` Permissions []RolePermissionsResponse `json:"permissions,omitempty"`
} }

View File

@ -2,15 +2,22 @@ package query
import ( import (
// "github.com/Caknoooo/go-gin-clean-starter/database/entities" // "github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/Caknoooo/go-gin-clean-starter/database/entities"
"github.com/Caknoooo/go-pagination" "github.com/Caknoooo/go-pagination"
"gorm.io/gorm" "gorm.io/gorm"
) )
type M_Role struct { type M_Role struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"`
IconUrl string `json:"icon_url"`
Type string `json:"type"`
HomeUrl string `json:"home_url"`
// Permissions []entities.M_Permissions `json:"permissions" gorm:"many2many:m_role_permissions;joinForeignKey:RoleID;JoinReferences:PermissionID"` // Permissions []entities.M_Permissions `json:"permissions" gorm:"many2many:m_role_permissions;joinForeignKey:RoleID;JoinReferences:PermissionID"`
ClientID string `json:"client_id"` ClientID string `json:"client_id"`
Client entities.M_Client `json:"client" gorm:"foreignKey:ClientID;references:ID"`
// Client pkgdto.IdNameResponse `json:"client"`
} }
type RoleFilter struct { type RoleFilter struct {
@ -27,6 +34,11 @@ func (f *RoleFilter) ApplyFilters(query *gorm.DB) *gorm.DB {
if f.ClientID != "" { if f.ClientID != "" {
query = query.Where("client_id = ?", f.ClientID) query = query.Where("client_id = ?", f.ClientID)
} }
query = query.Model(entities.M_Role{}).
Preload("Client", func(db *gorm.DB) *gorm.DB {
return db.Select("m_clients.id", "m_clients.name")
})
return query return query
} }

View File

@ -127,6 +127,7 @@ func (r *roleRepository) GetRoleByID(ctx context.Context, tx *gorm.DB, id string
var role entities.M_Role var role entities.M_Role
if err := tx.WithContext(ctx). if err := tx.WithContext(ctx).
Where("id = ?", id). Where("id = ?", id).
Preload("Client").
Preload("Permissions"). Preload("Permissions").
First(&role).Error; err != nil { First(&role).Error; err != nil {
return entities.M_Role{}, err return entities.M_Role{}, err
@ -144,6 +145,7 @@ func (r *roleRepository) GetRolesByUserID(ctx context.Context, tx *gorm.DB, user
Model(&entities.M_Role{}). Model(&entities.M_Role{}).
Joins("JOIN m_user_roles ur ON ur.role_id = m_roles.id"). Joins("JOIN m_user_roles ur ON ur.role_id = m_roles.id").
Where("ur.user_id = ?", userId). Where("ur.user_id = ?", userId).
Preload("Client").
Preload("Permissions"). Preload("Permissions").
Find(&roles).Error; err != nil { Find(&roles).Error; err != nil {
return nil, err return nil, err

View File

@ -11,6 +11,7 @@ import (
"github.com/Caknoooo/go-gin-clean-starter/modules/role/repository" "github.com/Caknoooo/go-gin-clean-starter/modules/role/repository"
userDto "github.com/Caknoooo/go-gin-clean-starter/modules/user/dto" userDto "github.com/Caknoooo/go-gin-clean-starter/modules/user/dto"
userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service" userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service"
pkgdto "github.com/Caknoooo/go-gin-clean-starter/pkg/dto"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"gorm.io/gorm" "gorm.io/gorm"
@ -182,15 +183,13 @@ func (r *roleService) CreateRole(ctx context.Context, req dto.RoleCreateRequest)
if err != nil { if err != nil {
return dto.RoleResponse{}, err return dto.RoleResponse{}, err
} }
return dto.RoleResponse{
ID: createdRole.ID.String(), result, err := r.roleRepo.GetRoleByID(ctx, r.db, createdRole.ID.String())
Name: createdRole.Name, if err != nil {
Description: createdRole.Description, return dto.RoleResponse{}, err
IconUrl: createdRole.IconUrl, }
Type: createdRole.Type, return ToRoleResponse(result), nil
HomeUrl: createdRole.HomeUrl,
ClientID: createdRole.ClientID.String(),
}, nil
} }
// DeleteRole implements RoleService. // DeleteRole implements RoleService.
@ -208,24 +207,7 @@ func (r *roleService) GetRoleByID(ctx context.Context, id string) (dto.RoleRespo
return dto.RoleResponse{}, err return dto.RoleResponse{}, err
} }
var permissions []dto.RolePermissionsResponse return ToRoleResponse(role), nil
for _, p := range role.Permissions {
permissions = append(permissions, dto.RolePermissionsResponse{
ID: p.ID.String(),
Name: p.Name,
})
}
return dto.RoleResponse{
ID: role.ID.String(),
Name: role.Name,
Description: role.Description,
IconUrl: role.IconUrl,
Type: role.Type,
HomeUrl: role.HomeUrl,
ClientID: role.ClientID.String(),
Permissions: permissions,
}, nil
} }
// GetRoles implements RoleService. // GetRoles implements RoleService.
@ -234,6 +216,44 @@ func (r *roleService) GetRoles(ctx context.Context, filter query.RoleFilter) ([]
} }
// GetRolesByUserID implements RoleService. // GetRolesByUserID implements RoleService.
// func (r *roleService) GetRolesByUserID(ctx context.Context, userId string) ([]dto.RoleResponse, error) {
// if _, err := r.userService.GetUserById(ctx, userId); err != nil {
// return nil, userDto.ErrUserNotFound
// }
// roles, err := r.roleRepo.GetRolesByUserID(ctx, r.db, userId)
// if err != nil {
// logrus.Error("Error fetching roles for user ", userId, ": ", err)
// return nil, err
// }
// logrus.Info("Fetched ", len(roles), " roles for user ", userId)
// var responses []dto.RoleResponse
// for _, role := range roles {
// var permissions []dto.RolePermissionsResponse
// for _, p := range role.Permissions {
// permissions = append(permissions, dto.RolePermissionsResponse{
// ID: p.ID.String(),
// Name: p.Name,
// })
// }
// responses = append(responses, dto.RoleResponse{
// ID: role.ID.String(),
// Name: role.Name,
// Description: role.Description,
// IconUrl: role.IconUrl,
// Type: role.Type,
// HomeUrl: role.HomeUrl,
// ClientID: role.ClientID.String(),
// Permissions: permissions,
// })
// }
// return responses, nil
// }
func (r *roleService) GetRolesByUserID(ctx context.Context, userId string) ([]dto.RoleResponse, error) { func (r *roleService) GetRolesByUserID(ctx context.Context, userId string) ([]dto.RoleResponse, error) {
if _, err := r.userService.GetUserById(ctx, userId); err != nil { if _, err := r.userService.GetUserById(ctx, userId); err != nil {
return nil, userDto.ErrUserNotFound return nil, userDto.ErrUserNotFound
@ -241,32 +261,12 @@ func (r *roleService) GetRolesByUserID(ctx context.Context, userId string) ([]dt
roles, err := r.roleRepo.GetRolesByUserID(ctx, r.db, userId) roles, err := r.roleRepo.GetRolesByUserID(ctx, r.db, userId)
if err != nil { if err != nil {
logrus.Error("Error fetching roles for user ", userId, ": ", err)
return nil, err return nil, err
} }
logrus.Info("Fetched ", len(roles), " roles for user ", userId)
var responses []dto.RoleResponse var responses []dto.RoleResponse
for _, role := range roles { for _, role := range roles {
var permissions []dto.RolePermissionsResponse responses = append(responses, ToRoleResponse(role))
for _, p := range role.Permissions {
permissions = append(permissions, dto.RolePermissionsResponse{
ID: p.ID.String(),
Name: p.Name,
})
}
responses = append(responses, dto.RoleResponse{
ID: role.ID.String(),
Name: role.Name,
Description: role.Description,
IconUrl: role.IconUrl,
Type: role.Type,
HomeUrl: role.HomeUrl,
ClientID: role.ClientID.String(),
Permissions: permissions,
})
} }
return responses, nil return responses, nil
@ -400,15 +400,41 @@ func (r *roleService) UpdateRole(ctx context.Context, id string, req dto.RoleUpd
return dto.RoleResponse{}, err return dto.RoleResponse{}, err
} }
result, err := r.roleRepo.GetRoleByID(ctx, r.db, updatedRole.ID.String())
if err != nil {
return dto.RoleResponse{}, err
}
return ToRoleResponse(result), nil
}
func ToRoleResponse(role entities.M_Role) dto.RoleResponse {
var permissions []dto.RolePermissionsResponse
for _, p := range role.Permissions {
permissions = append(permissions, dto.RolePermissionsResponse{
ID: p.ID.String(),
Name: p.Name,
})
}
var client pkgdto.IdNameResponse
if role.Client.ID != uuid.Nil {
client = pkgdto.IdNameResponse{
ID: role.Client.ID.String(),
Name: role.Client.Name,
}
}
return dto.RoleResponse{ return dto.RoleResponse{
ID: updatedRole.ID.String(), ID: role.ID.String(),
Name: updatedRole.Name, Name: role.Name,
Description: updatedRole.Description, Description: role.Description,
IconUrl: updatedRole.IconUrl, IconUrl: role.IconUrl,
Type: updatedRole.Type, Type: role.Type,
HomeUrl: updatedRole.HomeUrl, HomeUrl: role.HomeUrl,
ClientID: updatedRole.ClientID.String(), Client: client,
}, nil Permissions: permissions,
}
} }
func NewRoleService( func NewRoleService(