first commit
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package role
|
||||
|
||||
// RolPagesRequest represents the request payload for RolPages
|
||||
type RolPagesRequest struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url"`
|
||||
Level int16 `json:"level" validate:"required"`
|
||||
Sort int16 `json:"sort"`
|
||||
Parent *int64 `json:"parent"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
type RolPagesResponse struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url"`
|
||||
Level int16 `json:"level"`
|
||||
Sort int16 `json:"sort"`
|
||||
Parent *int64 `json:"parent"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// RoleAccessResponse represents the response structure for role access with hierarchical menu
|
||||
type RoleAccessResponse struct {
|
||||
Roles []string `json:"roles"`
|
||||
Access []MenuItem `json:"access"`
|
||||
}
|
||||
|
||||
// MenuItem represents a menu item in the access hierarchy
|
||||
type MenuItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url"`
|
||||
Level string `json:"level"`
|
||||
Children []MenuChild `json:"children"`
|
||||
}
|
||||
|
||||
// MenuChild represents a child menu item
|
||||
type MenuChild struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package role
|
||||
|
||||
// RolPages entity represents the rol_pages table in the database
|
||||
type RolPages struct {
|
||||
Id int64 `json:"id" db:"id" gorm:"primaryKey;autoIncrement"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Icon string `json:"icon" db:"icon"`
|
||||
URL string `json:"url" db:"url"`
|
||||
Level int16 `json:"level" db:"level"`
|
||||
Sort int16 `json:"sort" db:"sort"`
|
||||
Parent *int64 `json:"parent" db:"parent"`
|
||||
Active bool `json:"active" db:"active"`
|
||||
}
|
||||
|
||||
// TableName specifies the table name for RolPages
|
||||
func (RolPages) TableName() string {
|
||||
return "role_access.rol_pages"
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
rolepPermission "service/internal/master/role/permission"
|
||||
)
|
||||
|
||||
func mapRequestToEntity(req RolPagesRequest) *RolPages {
|
||||
return &RolPages{
|
||||
Name: req.Name,
|
||||
Icon: req.Icon,
|
||||
URL: req.URL,
|
||||
Level: req.Level,
|
||||
Sort: req.Sort,
|
||||
Parent: req.Parent,
|
||||
Active: req.Active,
|
||||
}
|
||||
}
|
||||
|
||||
func mapEntityToResponse(e *RolPages) *RolPagesResponse {
|
||||
return &RolPagesResponse{
|
||||
Id: e.Id,
|
||||
Name: e.Name,
|
||||
Icon: e.Icon,
|
||||
URL: e.URL,
|
||||
Level: e.Level,
|
||||
Sort: e.Sort,
|
||||
Parent: e.Parent,
|
||||
Active: e.Active,
|
||||
}
|
||||
}
|
||||
|
||||
// MapPermissionToRoleAccessResponse converts RolPermission to RoleAccessResponse structure
|
||||
func MapPermissionToRoleAccessResponse(roles []string, permissions []*rolepPermission.RolPermission, pages []*RolPages) *RoleAccessResponse {
|
||||
response := &RoleAccessResponse{
|
||||
Roles: roles,
|
||||
Access: []MenuItem{},
|
||||
}
|
||||
|
||||
// Map permissions to create menu items based on pages
|
||||
// Filter pages yang memiliki permission read = true
|
||||
accessiblePages := []*RolPages{}
|
||||
for i, permission := range permissions {
|
||||
if permission.Read && i < len(pages) {
|
||||
accessiblePages = append(accessiblePages, pages[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Group pages by parent ID
|
||||
parentMap := make(map[int64][]*RolPages)
|
||||
var rootPages []*RolPages
|
||||
|
||||
for _, page := range accessiblePages {
|
||||
if page.Parent == nil {
|
||||
rootPages = append(rootPages, page)
|
||||
} else {
|
||||
parentMap[*page.Parent] = append(parentMap[*page.Parent], page)
|
||||
}
|
||||
}
|
||||
|
||||
// Build menu hierarchy
|
||||
for _, rootPage := range rootPages {
|
||||
menuItem := MenuItem{
|
||||
ID: generateUUID(),
|
||||
Name: rootPage.Name,
|
||||
Icon: rootPage.Icon,
|
||||
URL: rootPage.URL,
|
||||
Level: fmt.Sprintf("%d", rootPage.Level),
|
||||
}
|
||||
|
||||
// Add children if any
|
||||
if children, exists := parentMap[rootPage.Id]; exists {
|
||||
for _, child := range children {
|
||||
menuChild := MenuChild{
|
||||
Name: child.Name,
|
||||
Icon: child.Icon,
|
||||
URL: child.URL,
|
||||
Level: fmt.Sprintf("%d", child.Level),
|
||||
}
|
||||
menuItem.Children = append(menuItem.Children, menuChild)
|
||||
}
|
||||
}
|
||||
|
||||
response.Access = append(response.Access, menuItem)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// Helper function to generate UUID (you may want to use a proper UUID library)
|
||||
func generateUUID() string {
|
||||
return fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"service/pkg/logger"
|
||||
"service/pkg/utils/query"
|
||||
|
||||
rolepPermission "service/internal/master/role/permission"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RoleAccessRepository defines the contract for role access data operations
|
||||
type RoleAccessRepository interface {
|
||||
// GetRoleAccess retrieves role access information for a specific user
|
||||
GetRoleAccess(ctx context.Context, userID string) (*RoleAccessData, error)
|
||||
// GetRolePermissions retrieves permissions for a specific role
|
||||
GetRolePermissions(ctx context.Context, roleID int64) ([]*rolepPermission.RolPermission, error)
|
||||
// GetRolePages retrieves pages accessible by role
|
||||
GetRolePages(ctx context.Context, roleIDs []int64) ([]*RolPages, error)
|
||||
// GetUserRoles retrieves roles for a specific user
|
||||
GetUserRoles(ctx context.Context, userID string) ([]string, error)
|
||||
}
|
||||
|
||||
// RoleAccessData represents the complete role access information
|
||||
type RoleAccessData struct {
|
||||
Roles []string
|
||||
Permissions []*rolepPermission.RolPermission
|
||||
Pages []*RolPages
|
||||
}
|
||||
|
||||
type roleAccessRepository struct {
|
||||
db *gorm.DB
|
||||
qb query.QueryBuilder
|
||||
dbType query.DBType
|
||||
}
|
||||
|
||||
// NewRoleAccessRepository creates a new instance of RoleAccessRepository
|
||||
func NewRoleAccessRepository(db *gorm.DB) RoleAccessRepository {
|
||||
dbType := query.DBTypePostgreSQL
|
||||
qb := query.NewSQLQueryBuilder(dbType).
|
||||
SetSecurityOptions(true, 1000).
|
||||
SetQueryLogging(true).
|
||||
SetQueryTimeout(30)
|
||||
|
||||
return &roleAccessRepository{
|
||||
db: db,
|
||||
qb: qb,
|
||||
dbType: dbType,
|
||||
}
|
||||
}
|
||||
|
||||
// getSQLXDB extracts *sqlx.DB from *gorm.DB
|
||||
func (r *roleAccessRepository) getSQLXDB() (*sqlx.DB, error) {
|
||||
sqlDB, err := r.db.DB()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get underlying *sql.DB: %w", err)
|
||||
}
|
||||
return sqlx.NewDb(sqlDB, "pgx"), nil
|
||||
}
|
||||
|
||||
// GetRoleAccess retrieves complete role access information for a user
|
||||
func (r *roleAccessRepository) GetRoleAccess(ctx context.Context, userID string) (*RoleAccessData, error) {
|
||||
// Get user roles
|
||||
roles, err := r.GetUserRoles(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user roles: %w", err)
|
||||
}
|
||||
|
||||
if len(roles) == 0 {
|
||||
return &RoleAccessData{
|
||||
Roles: []string{},
|
||||
Permissions: []*rolepPermission.RolPermission{},
|
||||
Pages: []*RolPages{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// For now, we'll use mock role IDs - in real implementation, this should come from database
|
||||
// Mock role IDs based on role names
|
||||
var roleIDs []int64
|
||||
for range roles {
|
||||
roleIDs = append(roleIDs, 1) // Mock role ID
|
||||
}
|
||||
|
||||
// Get permissions and pages for the roles
|
||||
permissions, err := r.GetRolePermissions(ctx, 1) // Mock role ID
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get role permissions: %w", err)
|
||||
}
|
||||
|
||||
pages, err := r.GetRolePages(ctx, roleIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get role pages: %w", err)
|
||||
}
|
||||
|
||||
return &RoleAccessData{
|
||||
Roles: roles,
|
||||
Permissions: permissions,
|
||||
Pages: pages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetRolePermissions retrieves permissions for a specific role
|
||||
func (r *roleAccessRepository) GetRolePermissions(ctx context.Context, roleID int64) ([]*rolepPermission.RolPermission, error) {
|
||||
sqlxDB, err := r.getSQLXDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For now, return all permissions - in real implementation, this should filter by role
|
||||
q := query.DynamicQuery{
|
||||
From: "rol_permission",
|
||||
Filters: []query.FilterGroup{{
|
||||
Filters: []query.DynamicFilter{
|
||||
query.CreateFilter("DeletedAt", query.OpNull, nil),
|
||||
},
|
||||
}},
|
||||
Limit: 100,
|
||||
}
|
||||
|
||||
var results []*rolepPermission.RolPermission
|
||||
if err := r.qb.ExecuteQuery(ctx, sqlxDB, q, &results); err != nil {
|
||||
return nil, fmt.Errorf("failed to execute role permissions query: %w", err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetRolePages retrieves pages accessible by role
|
||||
func (r *roleAccessRepository) GetRolePages(ctx context.Context, roleIDs []int64) ([]*RolPages, error) {
|
||||
sqlxDB, err := r.getSQLXDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all active pages - in real implementation, this should join with role-page permissions
|
||||
q := query.DynamicQuery{
|
||||
From: "role_access.rol_pages",
|
||||
Filters: []query.FilterGroup{{
|
||||
Filters: []query.DynamicFilter{
|
||||
query.CreateEqualFilter("Active", true),
|
||||
},
|
||||
}},
|
||||
Sort: []query.SortField{
|
||||
query.CreateAscSort("Level"),
|
||||
query.CreateAscSort("Sort"),
|
||||
},
|
||||
Limit: 100,
|
||||
}
|
||||
|
||||
var results []*RolPages
|
||||
if err := r.qb.ExecuteQuery(ctx, sqlxDB, q, &results); err != nil {
|
||||
return nil, fmt.Errorf("failed to execute role pages query: %w", err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetUserRoles retrieves roles for a specific user
|
||||
func (r *roleAccessRepository) GetUserRoles(ctx context.Context, userID string) ([]string, error) {
|
||||
// Mock implementation - in real implementation, this should query user-role mapping table
|
||||
// For now, return mock roles based on user ID
|
||||
logger.Default().Info("Getting user roles", logger.String("userID", userID))
|
||||
|
||||
// Mock roles - in real implementation, get from database
|
||||
mockRoles := []string{"admin1", "admin2"}
|
||||
|
||||
return mockRoles, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"context"
|
||||
"service/pkg/errors"
|
||||
)
|
||||
|
||||
// RoleAccessService defines the contract for role access operations
|
||||
type RoleAccessService interface {
|
||||
GetRoleAccess(ctx context.Context, userID string) (*RoleAccessResponse, error)
|
||||
}
|
||||
|
||||
// roleAccessService implements RoleAccessService
|
||||
type roleAccessService struct {
|
||||
repo RoleAccessRepository
|
||||
}
|
||||
|
||||
// NewRoleAccessService creates a new instance of RoleAccessService
|
||||
func NewRoleAccessService(repo RoleAccessRepository) RoleAccessService {
|
||||
return &roleAccessService{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRoleAccess retrieves role access information for a user
|
||||
func (s *roleAccessService) GetRoleAccess(ctx context.Context, userID string) (*RoleAccessResponse, error) {
|
||||
if userID == "" {
|
||||
return nil, errors.NewValidationError().Message("User ID is required").Metadata("userID", userID).Build()
|
||||
}
|
||||
|
||||
// Get role access data from repository
|
||||
roleAccessData, err := s.repo.GetRoleAccess(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.InternalError().Message("Failed to retrieve role access data").Cause(err).Build()
|
||||
}
|
||||
|
||||
// Map the data to response format
|
||||
response := MapPermissionToRoleAccessResponse(
|
||||
roleAccessData.Roles,
|
||||
roleAccessData.Permissions,
|
||||
roleAccessData.Pages,
|
||||
)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetUserRoles retrieves roles for a specific user
|
||||
func (s *roleAccessService) GetUserRoles(ctx context.Context, userID string) ([]string, error) {
|
||||
if userID == "" {
|
||||
return nil, errors.NewValidationError().Message("User ID is required").Metadata("userID", userID).Build()
|
||||
}
|
||||
|
||||
return s.repo.GetUserRoles(ctx, userID)
|
||||
}
|
||||
Reference in New Issue
Block a user