Skip to content

Commit

Permalink
feat: adding categories CRUD for admin
Browse files Browse the repository at this point in the history
  • Loading branch information
felipebrsk committed Oct 20, 2024
1 parent edf1abf commit 313c4ea
Show file tree
Hide file tree
Showing 14 changed files with 798 additions and 32 deletions.
5 changes: 5 additions & 0 deletions DONE.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,10 @@
- [x] Create me method for admin
- [x] Return user permissions
- [x] Return user roles
- [x] Create categories CRUD
- [x] Create method to list all categories
- [x] Create method to create a new category
- [x] Create method to update a category
- [x] Create method to delete a category

### Post MVP
2 changes: 2 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func main() {
missionService,
gameService,
bannerService,
adminCategoryService,
db := di.InitDependencies()

// Setup routes with dependency injection
Expand All @@ -46,6 +47,7 @@ func main() {
missionService,
gameService,
bannerService,
adminCategoryService,
db,
)

Expand Down
7 changes: 6 additions & 1 deletion cmd/server/routes/init_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"gcstatus/internal/adapters/api"
api_admin "gcstatus/internal/adapters/api/admin"
"gcstatus/internal/usecases"
usecases_admin "gcstatus/internal/usecases/admin"

"gorm.io/gorm"
)
Expand All @@ -22,6 +23,7 @@ func InitHandlers(
missionService *usecases.MissionService,
gameService *usecases.GameService,
bannerService *usecases.BannerService,
adminCategoryService *usecases_admin.AdminCategoryService,
db *gorm.DB,
) (
authHandler *api.AuthHandler,
Expand All @@ -37,6 +39,7 @@ func InitHandlers(
homeHandler *api.HomeHandler,
steamHandler *api_admin.SteamHandler,
adminAuthHandler *api_admin.AuthHandler,
adminCategoryHandler *api_admin.AdminCategoryHandler,
) {
userHandler = api.NewUserHandler(userService)
authHandler = api.NewAuthHandler(authService, userService)
Expand All @@ -51,6 +54,7 @@ func InitHandlers(
homeHandler = api.NewHomeHandler(userService, gameService, bannerService)
steamHandler = api_admin.NewSteamHandler(gameService, db)
adminAuthHandler = api_admin.NewAuthHandler(authService, userService)
adminCategoryHandler = api_admin.NewAdminCategoryHandler(adminCategoryService)

return authHandler,
passwordResetHandler,
Expand All @@ -64,5 +68,6 @@ func InitHandlers(
gameHandler,
homeHandler,
steamHandler,
adminAuthHandler
adminAuthHandler,
adminCategoryHandler
}
14 changes: 12 additions & 2 deletions cmd/server/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"gcstatus/config"
"gcstatus/internal/middlewares"
"gcstatus/internal/usecases"
usecases_admin "gcstatus/internal/usecases/admin"
"strings"

"github.com/gin-contrib/cors"
Expand All @@ -26,6 +27,7 @@ func SetupRouter(
missionService *usecases.MissionService,
gameService *usecases.GameService,
bannerService *usecases.BannerService,
adminCategoryService *usecases_admin.AdminCategoryService,
db *gorm.DB,
) *gin.Engine {
r := gin.Default()
Expand Down Expand Up @@ -59,7 +61,8 @@ func SetupRouter(
gameHandler,
homeHandler,
steamHandler,
adminAuthHandler := InitHandlers(
adminAuthHandler,
adminCategoryHandler := InitHandlers(
authService,
userService,
passwordResetService,
Expand All @@ -73,11 +76,13 @@ func SetupRouter(
missionService,
gameService,
bannerService,
adminCategoryService,
db,
)

// Define the middlewares
r.Use(middlewares.LimitThrottleMiddleware())
permissionMiddleware := middlewares.NewPermissionMiddleware(userService)
protected := r.Group("/")
admin := r.Group("/admin")

Expand Down Expand Up @@ -127,7 +132,12 @@ func SetupRouter(
{
admin.GET("/me", adminAuthHandler.Me)
admin.POST("/logout", adminAuthHandler.Logout)
admin.POST("/steam/register/:appID", middlewares.PermissionMiddleware(userService, "create:steam-jobs-games"), steamHandler.RegisterByAppID)
admin.POST("/steam/register/:appID", permissionMiddleware("create:steam-jobs-games"), steamHandler.RegisterByAppID)

admin.GET("/categories", permissionMiddleware("view:categories"), adminCategoryHandler.GetAll)
admin.POST("/categories", permissionMiddleware("view:categories", "create:categories"), adminCategoryHandler.Create)
admin.PUT("/categories/:id", permissionMiddleware("view:categories", "update:categories"), adminCategoryHandler.Update)
admin.DELETE("/categories/:id", permissionMiddleware("view:categories", "delete:categories"), adminCategoryHandler.Delete)
}

// Common routes
Expand Down
6 changes: 5 additions & 1 deletion di/di.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"gcstatus/config"
"gcstatus/internal/usecases"
usecases_admin "gcstatus/internal/usecases/admin"
"gcstatus/pkg/cache"
"gcstatus/pkg/s3"
"gcstatus/pkg/sqs"
Expand All @@ -27,6 +28,7 @@ func InitDependencies() (
*usecases.MissionService,
*usecases.GameService,
*usecases.BannerService,
*usecases_admin.AdminCategoryService,
*gorm.DB,
) {
cfg := config.LoadConfig()
Expand Down Expand Up @@ -54,7 +56,8 @@ func InitDependencies() (
notificationService,
missionService,
gameService,
bannerService := Setup(dbConn)
bannerService,
adminCategoryService := Setup(dbConn)

// Setup clients for non-test environment
if cfg.ENV != "testing" {
Expand Down Expand Up @@ -90,5 +93,6 @@ func InitDependencies() (
missionService,
gameService,
bannerService,
adminCategoryService,
dbConn
}
8 changes: 7 additions & 1 deletion di/setup_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package di

import (
"gcstatus/internal/adapters/db"
db_admin "gcstatus/internal/adapters/db/admin"
"gcstatus/internal/usecases"
usecases_admin "gcstatus/internal/usecases/admin"

"gorm.io/gorm"
)
Expand All @@ -21,6 +23,7 @@ func Setup(dbConn *gorm.DB) (
*usecases.MissionService,
*usecases.GameService,
*usecases.BannerService,
*usecases_admin.AdminCategoryService,
) {
// Create repository instances
userRepo := db.NewUserRepositoryMySQL(dbConn)
Expand All @@ -35,6 +38,7 @@ func Setup(dbConn *gorm.DB) (
missionRepo := db.NewMissionRepositoryMySQL(dbConn)
gameRepo := db.NewGameRepositoryMySQL(dbConn)
bannerRepo := db.NewBannerRepositoryMySQL(dbConn)
adminCategoryRepo := db_admin.NewAdminCategoryRepositoryMySQL(dbConn)

// Create service instances
userService := usecases.NewUserService(userRepo)
Expand All @@ -50,6 +54,7 @@ func Setup(dbConn *gorm.DB) (
missionService := usecases.NewMissionService(missionRepo)
gameService := usecases.NewGameService(gameRepo)
bannerService := usecases.NewBannerService(bannerRepo)
adminCategoryService := usecases_admin.NewAdminCategoryService(adminCategoryRepo)

return userService,
authService,
Expand All @@ -63,5 +68,6 @@ func Setup(dbConn *gorm.DB) (
notificationService,
missionService,
gameService,
bannerService
bannerService,
adminCategoryService
}
109 changes: 109 additions & 0 deletions internal/adapters/api/admin/category_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package api_admin

import (
"gcstatus/internal/adapters/api"
"gcstatus/internal/domain"
"gcstatus/internal/errors"
ports_admin "gcstatus/internal/ports/admin"
"gcstatus/internal/resources"
usecases_admin "gcstatus/internal/usecases/admin"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
)

type AdminCategoryHandler struct {
categoryService *usecases_admin.AdminCategoryService
}

func NewAdminCategoryHandler(
categoryService *usecases_admin.AdminCategoryService,
) *AdminCategoryHandler {
return &AdminCategoryHandler{
categoryService: categoryService,
}
}

func (h *AdminCategoryHandler) GetAll(c *gin.Context) {
categories, err := h.categoryService.GetAll()
if err != nil {
api.RespondWithError(c, http.StatusInternalServerError, "Failed to fetch categories: "+err.Error())
return
}

transformedCategories := resources.TransformCategories(categories)

response := resources.Response{
Data: transformedCategories,
}

c.JSON(http.StatusOK, response)
}

func (h *AdminCategoryHandler) Create(c *gin.Context) {
var request struct {
Name string `json:"name" binding:"required"`
}

if err := c.ShouldBindJSON(&request); err != nil {
api.RespondWithError(c, http.StatusUnprocessableEntity, "Please, provide a category name.")
return
}

category := &domain.Category{
Name: request.Name,
}

if err := h.categoryService.Create(category); err != nil {
api.RespondWithError(c, http.StatusInternalServerError, "Failed to create category: "+err.Error())
return
}

c.JSON(http.StatusCreated, gin.H{"message": "The category was successfully created!"})
}

func (h *AdminCategoryHandler) Update(c *gin.Context) {
categoryIdStr := c.Param("id")

categoryID, err := strconv.ParseUint(categoryIdStr, 10, 32)
if err != nil {
api.RespondWithError(c, http.StatusBadRequest, "Invalid category ID: "+err.Error())
return
}

var request ports_admin.UpdateCategoryInterface

if err := c.ShouldBindJSON(&request); err != nil {
api.RespondWithError(c, http.StatusUnprocessableEntity, "Please, provide a category name.")
return
}

if err := h.categoryService.Update(uint(categoryID), request); err != nil {
api.RespondWithError(c, http.StatusInternalServerError, "Failed to update category: "+err.Error())
return
}

c.JSON(http.StatusOK, gin.H{"message": "The category was successfully updated!"})
}

func (h *AdminCategoryHandler) Delete(c *gin.Context) {
categoryIdStr := c.Param("id")

categoryID, err := strconv.ParseUint(categoryIdStr, 10, 32)
if err != nil {
api.RespondWithError(c, http.StatusBadRequest, "Invalid category ID: "+err.Error())
return
}

if err := h.categoryService.Delete(uint(categoryID)); err != nil {
if httpErr, ok := err.(*errors.HttpError); ok {
api.RespondWithError(c, httpErr.Code, httpErr.Error())
} else {
api.RespondWithError(c, http.StatusInternalServerError, "Failed to delete category: "+err.Error())
}
return
}

c.JSON(http.StatusOK, gin.H{"message": "The category was successfully removed!"})
}
54 changes: 54 additions & 0 deletions internal/adapters/db/admin/category_repository_mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package db_admin

import (
"fmt"
"gcstatus/internal/domain"
"gcstatus/internal/errors"
ports_admin "gcstatus/internal/ports/admin"
"net/http"

"gorm.io/gorm"
)

type AdminCategoryRepositoryMySQL struct {
db *gorm.DB
}

func NewAdminCategoryRepositoryMySQL(db *gorm.DB) ports_admin.AdminCategoryRepository {
return &AdminCategoryRepositoryMySQL{
db: db,
}
}

func (h *AdminCategoryRepositoryMySQL) GetAll() ([]domain.Category, error) {
var categories []domain.Category
err := h.db.Model(&domain.Category{}).
Find(&categories).
Error

return categories, err
}

func (h *AdminCategoryRepositoryMySQL) Create(category *domain.Category) error {
return h.db.Create(&category).Error
}

func (h *AdminCategoryRepositoryMySQL) Update(id uint, request ports_admin.UpdateCategoryInterface) error {
updateFields := map[string]any{
"name": request.Name,
"slug": request.Slug,
}

if err := h.db.Model(&domain.Category{}).Where("id = ?", id).Updates(updateFields).Error; err != nil {
return fmt.Errorf("failed to update category: %+s", err.Error())
}

return nil
}

func (h *AdminCategoryRepositoryMySQL) Delete(id uint) error {
if err := h.db.Delete(&domain.Category{}, id).Error; err != nil {
return errors.NewHttpError(http.StatusNotFound, "category not found.")
}
return nil
}
Loading

0 comments on commit 313c4ea

Please sign in to comment.