diff --git a/cmd/main.go b/cmd/main.go index ebd83b0..c3053d7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -11,6 +11,7 @@ import ( "github.com/Caknoooo/go-gin-clean-starter/modules/client" maintenancegroup "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group" "github.com/Caknoooo/go-gin-clean-starter/modules/menu" + "github.com/Caknoooo/go-gin-clean-starter/modules/permissions" "github.com/Caknoooo/go-gin-clean-starter/modules/role" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" @@ -129,6 +130,7 @@ func main() { menu.RegisterRoutes(server, injector) maintenancegroup.RegisterRoutes(server, injector) client.RegisterRoutes(server, injector) + permissions.RegisterRoutes(server, injector) // register swagger route server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/modules/permissions/controller/permissions_controller.go b/modules/permissions/controller/permissions_controller.go new file mode 100644 index 0000000..29181cd --- /dev/null +++ b/modules/permissions/controller/permissions_controller.go @@ -0,0 +1,133 @@ +package controller + +import ( + "net/http" + + "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/dto" + "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/service" + "github.com/Caknoooo/go-gin-clean-starter/pkg/utils" + "github.com/gin-gonic/gin" +) + +type PermissionsController interface { + CreatePermission(ctx *gin.Context) + GetPermissionByID(ctx *gin.Context) + GetPermissionByName(ctx *gin.Context) + GetAllPermissions(ctx *gin.Context) + UpdatePermission(ctx *gin.Context) + DeletePermission(ctx *gin.Context) +} + +type permissionsController struct { + service service.PermissionsService +} + +func (c *permissionsController) CreatePermission(ctx *gin.Context) { + var req dto.PermissionsCreateRequest + if err := ctx.ShouldBind(&req); err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) + ctx.AbortWithStatusJSON(http.StatusBadRequest, res) + return + } + res, err := c.service.Create(ctx.Request.Context(), req) + if err != nil { + resp := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_PERMISSIONS, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, resp) + return + } + resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_CREATE_PERMISSIONS, res) + ctx.JSON(http.StatusOK, resp) +} + +func (c *permissionsController) GetPermissionByID(ctx *gin.Context) { + id := ctx.Param("id") + if id == "" { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_PERMISSIONS, "id is required", nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + res, err := c.service.GetById(ctx.Request.Context(), id) + if err != nil { + resErr := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_PERMISSIONS, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, resErr) + return + } + resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_PERMISSIONS, res) + ctx.JSON(http.StatusOK, resp) +} + +func (c *permissionsController) GetPermissionByName(ctx *gin.Context) { + name := ctx.Query("name") + if name == "" { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_PERMISSIONS, "name query is required", nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + res, err := c.service.GetByName(ctx.Request.Context(), name) + if err != nil { + resErr := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_PERMISSIONS, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, resErr) + return + } + resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_PERMISSIONS, res) + ctx.JSON(http.StatusOK, resp) +} + +func (c *permissionsController) GetAllPermissions(ctx *gin.Context) { + res, err := c.service.GetAll(ctx.Request.Context()) + if err != nil { + resp := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_PERMISSIONS, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, resp) + return + } + resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_GET_PERMISSIONS, res) + ctx.JSON(http.StatusOK, resp) +} + +func (c *permissionsController) UpdatePermission(ctx *gin.Context) { + var req dto.PermissionsUpdateRequest + if err := ctx.ShouldBind(&req); err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_GET_DATA_FROM_BODY, err.Error(), nil) + ctx.AbortWithStatusJSON(http.StatusBadRequest, res) + return + } + id := ctx.Param("id") + result, err := c.service.Update(ctx.Request.Context(), id, req) + if err != nil { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_UPDATE_PERMISSIONS, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_UPDATE_PERMISSIONS, result) + ctx.JSON(http.StatusOK, resp) +} + +func (c *permissionsController) DeletePermission(ctx *gin.Context) { + id := ctx.Param("id") + if id == "" { + res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_PERMISSIONS, "id is required", nil) + ctx.JSON(http.StatusBadRequest, res) + return + } + if err := c.service.Delete(ctx.Request.Context(), id); err != nil { + resp := utils.BuildResponseFailed(dto.MESSAGE_FAILED_DELETE_PERMISSIONS, err.Error(), nil) + ctx.JSON(http.StatusBadRequest, resp) + return + } + resp := utils.BuildResponseSuccess(dto.MESSAGE_SUCCESS_DELETE_PERMISSIONS, nil) + ctx.JSON(http.StatusOK, resp) +} + +func NewPermissionsController(s service.PermissionsService) PermissionsController { + return &permissionsController{ + service: s, + } +} + +// func NewPermissionsController(i *do.Injector, s service.PermissionsService) PermissionsController { +// db := do.MustInvokeNamed[*gorm.DB](i, constants.DB) +// return &permissionsController{ +// service: s, +// db: db, +// } +// } diff --git a/modules/permissions/dto/permissions_dto.go b/modules/permissions/dto/permissions_dto.go new file mode 100644 index 0000000..565d64e --- /dev/null +++ b/modules/permissions/dto/permissions_dto.go @@ -0,0 +1,48 @@ +package dto + +import ( + "errors" +) + +const ( + MESSAGE_FAILED_CREATE_PERMISSIONS = "failed create permissions" + MESSAGE_SUCCESS_CREATE_PERMISSIONS = "success create permissions" + MESSAGE_FAILED_GET_PERMISSIONS = "failed get permissions" + MESSAGE_SUCCESS_GET_PERMISSIONS = "success get permissions" + MESSAGE_FAILED_UPDATE_PERMISSIONS = "failed update permissions" + MESSAGE_FAILED_PROSES_REQUEST = "failed proses request" + MESSAGE_SUCCESS_UPDATE_PERMISSIONS = "success update permissions" + MESSAGE_FAILED_DELETE_PERMISSIONS = "failed delete permissions" + MESSAGE_SUCCESS_DELETE_PERMISSIONS = "success delete permissions" + MESSAGE_FAILED_GET_DATA_FROM_BODY = "failed get data from body" +) + +var ( + ErrCreatePermissions = errors.New("failed to create permissions") + ErrGetPermissionsById = errors.New("failed to get permissions by id") + ErrUpdatePermissions = errors.New("failed to update permissions") + ErrDeletePermissions = errors.New("failed to delete permissions") +) + +type ( + PermissionsCreateRequest struct { + Name string `json:"name" binding:"required"` + MenuID string `json:"menu_id" binding:"required,uuid"` + } + + PermissionsUpdateRequest struct { + Name string `json:"name" binding:"omitempty"` + MenuID string `json:"menu_id" binding:"omitempty,uuid"` + } + + PermissionsResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Menu Menu `json:"menu"` + } + + Menu struct { + ID string `json:"id"` + Name string `json:"name"` + } +) diff --git a/modules/permissions/query/permissions_query.go b/modules/permissions/query/permissions_query.go new file mode 100644 index 0000000..0fa2ea6 --- /dev/null +++ b/modules/permissions/query/permissions_query.go @@ -0,0 +1,49 @@ +package query + +import ( + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/dto" + "github.com/Caknoooo/go-pagination" + "github.com/google/uuid" + "gorm.io/gorm" +) + +type M_Permissions struct { + ID uuid.UUID `json:"id" gorm:"column:id"` + Name string `json:"name" gorm:"column:name"` + MenuID uuid.UUID `json:"menu_id" gorm:"column:menu_id"` + Menu dto.Menu `json:"menu" gorm:"foreignKey:MenuID;references:ID"` +} + +type PermissionsFilter struct { + pagination.BaseFilter + Name string `form:"name"` + MenuID string `form:"menu_id"` +} + +func (f *PermissionsFilter) ApplyFilters(q *gorm.DB) *gorm.DB { + if f.Name != "" { + q = q.Where("name ILIKE ?", "%"+f.Name+"%") + } + if f.MenuID != "" { + q = q.Where("menu_id = ?", f.MenuID) + } + q = q.Model(entities.M_Permissions{}).Preload("Menu") + return q +} + +func (f *PermissionsFilter) GetTableName() string { + return "m_permissions" +} + +func (f *PermissionsFilter) GetSearchFields() []string { + return []string{"name"} +} + +func (f *PermissionsFilter) GetDefaultSort() string { + return "id asc" +} + +func (f *PermissionsFilter) GetPagination() pagination.PaginationRequest { + return f.Pagination +} diff --git a/modules/permissions/repository/permissions_repository.go b/modules/permissions/repository/permissions_repository.go new file mode 100644 index 0000000..e02fba0 --- /dev/null +++ b/modules/permissions/repository/permissions_repository.go @@ -0,0 +1,90 @@ +package repository + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "gorm.io/gorm" +) + +type ( + PermissionsRepository interface { + Create(ctx context.Context, tx *gorm.DB, permission entities.M_Permissions) (entities.M_Permissions, error) + GetById(ctx context.Context, tx *gorm.DB, id string) (entities.M_Permissions, error) + GetByName(ctx context.Context, tx *gorm.DB, name string) (entities.M_Permissions, error) + Update(ctx context.Context, tx *gorm.DB, permission entities.M_Permissions) (entities.M_Permissions, error) + Delete(ctx context.Context, tx *gorm.DB, id string) error + GetAll(ctx context.Context, tx *gorm.DB) ([]entities.M_Permissions, error) + } + + permissionsRepository struct { + db *gorm.DB + } +) + +func (r *permissionsRepository) Create(ctx context.Context, tx *gorm.DB, permission entities.M_Permissions) (entities.M_Permissions, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Create(&permission).Error; err != nil { + return entities.M_Permissions{}, err + } + return permission, nil +} + +func (r *permissionsRepository) GetById(ctx context.Context, tx *gorm.DB, id string) (entities.M_Permissions, error) { + if tx == nil { + tx = r.db + } + var permission entities.M_Permissions + if err := tx.WithContext(ctx).Preload("Menu").First(&permission, "id = ?", id).Error; err != nil { + return entities.M_Permissions{}, err + } + return permission, nil +} + +func (r *permissionsRepository) GetByName(ctx context.Context, tx *gorm.DB, name string) (entities.M_Permissions, error) { + if tx == nil { + tx = r.db + } + var permission entities.M_Permissions + if err := tx.WithContext(ctx).Where("name = ?", name).First(&permission).Error; err != nil { + return entities.M_Permissions{}, err + } + return permission, nil +} + +func (r *permissionsRepository) Update(ctx context.Context, tx *gorm.DB, permission entities.M_Permissions) (entities.M_Permissions, error) { + if tx == nil { + tx = r.db + } + if err := tx.WithContext(ctx).Updates(&permission).Error; err != nil { + return entities.M_Permissions{}, err + } + return permission, nil +} + +func (r *permissionsRepository) Delete(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_Permissions{}).Error; err != nil { + return err + } + return nil +} + +func (r *permissionsRepository) GetAll(ctx context.Context, tx *gorm.DB) ([]entities.M_Permissions, error) { + if tx == nil { + tx = r.db + } + var permissions []entities.M_Permissions + if err := tx.WithContext(ctx).Preload("Menu").Find(&permissions).Error; err != nil { + return nil, err + } + return permissions, nil +} + +func NewPermissionsRepository(db *gorm.DB) PermissionsRepository { + return &permissionsRepository{db: db} +} diff --git a/modules/permissions/routes.go b/modules/permissions/routes.go new file mode 100644 index 0000000..eb1e35b --- /dev/null +++ b/modules/permissions/routes.go @@ -0,0 +1,24 @@ +package permissions + +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/permissions/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) { + permissionsController := do.MustInvoke[controller.PermissionsController](injector) + jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService) + + permissionsRoutes := server.Group("/api/v1/permissions") + { + permissionsRoutes.GET("", middlewares.Authenticate(jwtService), permissionsController.GetAllPermissions) + permissionsRoutes.POST("", middlewares.Authenticate(jwtService), permissionsController.CreatePermission) + permissionsRoutes.GET("/:id", middlewares.Authenticate(jwtService), permissionsController.GetPermissionByID) + permissionsRoutes.PUT("/:id", middlewares.Authenticate(jwtService), permissionsController.UpdatePermission) + permissionsRoutes.DELETE("/:id", middlewares.Authenticate(jwtService), permissionsController.DeletePermission) + } +} diff --git a/modules/permissions/service/permissions_service.go b/modules/permissions/service/permissions_service.go new file mode 100644 index 0000000..083b492 --- /dev/null +++ b/modules/permissions/service/permissions_service.go @@ -0,0 +1,115 @@ +package service + +import ( + "context" + + "github.com/Caknoooo/go-gin-clean-starter/database/entities" + "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/dto" + "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/repository" + "github.com/google/uuid" + "gorm.io/gorm" +) + +type PermissionsService interface { + Create(ctx context.Context, req dto.PermissionsCreateRequest) (dto.PermissionsResponse, error) + GetById(ctx context.Context, id string) (dto.PermissionsResponse, error) + GetByName(ctx context.Context, name string) (dto.PermissionsResponse, error) + Update(ctx context.Context, id string, req dto.PermissionsUpdateRequest) (dto.PermissionsResponse, error) + Delete(ctx context.Context, id string) error + GetAll(ctx context.Context) ([]dto.PermissionsResponse, error) +} + +type permissionsService struct { + repo repository.PermissionsRepository + db *gorm.DB +} + +func (s *permissionsService) Create(ctx context.Context, req dto.PermissionsCreateRequest) (dto.PermissionsResponse, error) { + menuID, err := uuid.Parse(req.MenuID) + if err != nil { + return dto.PermissionsResponse{}, err + } + entity := entities.M_Permissions{ + Name: req.Name, + MenuID: menuID, + } + created, err := s.repo.Create(ctx, s.db, entity) + if err != nil { + return dto.PermissionsResponse{}, err + } + // Ambil ulang dengan preload agar relasi Menu terisi + createdWithMenu, err := s.repo.GetById(ctx, s.db, created.ID.String()) + if err != nil { + return dto.PermissionsResponse{}, err + } + return toPermissionsResponse(createdWithMenu), nil +} + +func (s *permissionsService) GetById(ctx context.Context, id string) (dto.PermissionsResponse, error) { + entity, err := s.repo.GetById(ctx, s.db, id) + if err != nil { + return dto.PermissionsResponse{}, err + } + return toPermissionsResponse(entity), nil +} + +func (s *permissionsService) GetByName(ctx context.Context, name string) (dto.PermissionsResponse, error) { + entity, err := s.repo.GetByName(ctx, s.db, name) + if err != nil { + return dto.PermissionsResponse{}, err + } + return toPermissionsResponse(entity), nil +} + +func (s *permissionsService) Update(ctx context.Context, id string, req dto.PermissionsUpdateRequest) (dto.PermissionsResponse, error) { + existing, err := s.repo.GetById(ctx, s.db, id) + if err != nil { + return dto.PermissionsResponse{}, dto.ErrGetPermissionsById + } + if req.Name != "" { + existing.Name = req.Name + } + if req.MenuID != "" { + menuID, err := uuid.Parse(req.MenuID) + if err != nil { + return dto.PermissionsResponse{}, err + } + existing.MenuID = menuID + } + updated, err := s.repo.Update(ctx, s.db, existing) + if err != nil { + return dto.PermissionsResponse{}, err + } + return toPermissionsResponse(updated), nil +} + +func (s *permissionsService) Delete(ctx context.Context, id string) error { + return s.repo.Delete(ctx, s.db, id) +} + +func (s *permissionsService) GetAll(ctx context.Context) ([]dto.PermissionsResponse, error) { + entities, err := s.repo.GetAll(ctx, s.db) + if err != nil { + return nil, err + } + var res []dto.PermissionsResponse + for _, e := range entities { + res = append(res, toPermissionsResponse(e)) + } + return res, nil +} + +func NewPermissionsService(repo repository.PermissionsRepository, db *gorm.DB) PermissionsService { + return &permissionsService{repo: repo, db: db} +} + +func toPermissionsResponse(e entities.M_Permissions) dto.PermissionsResponse { + return dto.PermissionsResponse{ + ID: e.ID.String(), + Name: e.Name, + Menu: dto.Menu{ + ID: e.Menu.ID.String(), + Name: e.Menu.Name, + }, + } +} diff --git a/providers/core.go b/providers/core.go index 4ea610b..42518a4 100644 --- a/providers/core.go +++ b/providers/core.go @@ -20,6 +20,10 @@ import ( roleRepo "github.com/Caknoooo/go-gin-clean-starter/modules/role/repository" roleService "github.com/Caknoooo/go-gin-clean-starter/modules/role/service" + permissionsController "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/controller" + permissionsRepo "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/repository" + permissionsService "github.com/Caknoooo/go-gin-clean-starter/modules/permissions/service" + maintGroupController "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group/controller" maintGroupRepo "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group/repository" maintGroupRepoRole "github.com/Caknoooo/go-gin-clean-starter/modules/maintenance_group/repository" @@ -67,6 +71,7 @@ func RegisterDependencies(injector *do.Injector) { maintenanceGroupRepository := maintGroupRepo.NewMaintGroupRepository(db) maintenanceGroupRoleRepository := maintGroupRepoRole.NewMaintGroupRoleRepository(db) maintenanceGroupRoleUserRepository := maintGroupRepoRoleUser.NewMaintGroupRoleUserRepository(db) + permissionsRepository := permissionsRepo.NewPermissionsRepository(db) // Service userServ := userService.NewUserService(userRepository, refreshTokenRepository, jwtService, db) @@ -75,6 +80,7 @@ func RegisterDependencies(injector *do.Injector) { menuSvc := menuService.NewMenuService(menuRepository, jwtService, db) maintenanceGroupServ := maintGroupService.NewMaintenanceGroupService(maintenanceGroupRepository, maintenanceGroupRoleRepository, maintenanceGroupRoleUserRepository, db) clientServ := clientService.NewClientService(clientRepository, db) + permissionsServ := permissionsService.NewPermissionsService(permissionsRepository, db) // Controller do.Provide( @@ -117,4 +123,9 @@ func RegisterDependencies(injector *do.Injector) { return maintGroupController.NewMaintenanceGroupController(i, maintenanceGroupServ), nil }, ) + do.Provide( + injector, func(i *do.Injector) (permissionsController.PermissionsController, error) { + return permissionsController.NewPermissionsController(permissionsServ), nil + }, + ) }