Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/ofrendialsa/neromerce/middlewares"
"github.com/ofrendialsa/neromerce/modules/auth"
"github.com/ofrendialsa/neromerce/modules/category"
products "github.com/ofrendialsa/neromerce/modules/product"
"github.com/ofrendialsa/neromerce/modules/user"
"github.com/ofrendialsa/neromerce/providers"
"github.com/ofrendialsa/neromerce/script"
Expand Down Expand Up @@ -66,6 +67,7 @@ func main() {
user.RegisterRoutes(server, injector)
auth.RegisterRoutes(server, injector)
category.RegisterRoutes(server, injector)
products.RegisterRoutes(server, injector)

run(server)
}
109 changes: 108 additions & 1 deletion modules/product/controller/product_controller.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
package controller

import (
"errors"
"net/http"

"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/ofrendialsa/neromerce/modules/product/dto"
"github.com/ofrendialsa/neromerce/modules/product/service"
"github.com/ofrendialsa/neromerce/modules/product/validation"
"github.com/ofrendialsa/neromerce/pkg/constants"
"github.com/ofrendialsa/neromerce/pkg/utils"
"github.com/samber/do"
"gorm.io/gorm"
)

type (
ProductController interface {
Create(ctx *gin.Context)
GetById(ctx *gin.Context)
Update(ctx *gin.Context)
GetAll(ctx *gin.Context)
Delete(ctx *gin.Context)
}

productController struct {
Expand All @@ -19,7 +31,102 @@ type (
}
)

func NewProductsController(injector *do.Injector, s service.ProductService) ProductController {
func (p *productController) Create(ctx *gin.Context) {
var req dto.ProductCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
res := utils.BuildResponseFailed(
dto.MESSAGE_FAILED_GET_DATA_FROM_BODY,
"request format invalid",
nil,
)
ctx.JSON(http.StatusBadRequest, res)
return
}

if err := p.productValidation.ValidateCreateRequest(req); err != nil {
if ve, ok := err.(validator.ValidationErrors); ok {
e := ve[0]
var msg string
switch e.Tag() {
case "required":
msg = "product name is required"
case "name":
msg = "product name must be at most 100 characters"
}
res := utils.BuildResponseFailed(dto.MESSAGE_FAILED_CREATE_PRODUCT, msg, nil)
ctx.JSON(http.StatusBadRequest, res)
return
}
}

product, err := p.productService.Create(ctx.Request.Context(), req)
if err != nil {

switch {
case errors.Is(err, dto.ErrProductNameExist):
res := utils.BuildResponseFailed(
dto.MESSAGE_FAILED_CREATE_PRODUCT,
"product already exists",
nil,
)
ctx.JSON(http.StatusConflict, res)
return

default:
res := utils.BuildResponseFailed(
dto.MESSAGE_FAILED_CREATE_PRODUCT,
"internal server error",
nil,
)
ctx.JSON(http.StatusInternalServerError, res)
return
}
}

res := utils.BuildResponseSuccess(
dto.MESSAGE_SUCCESS_CREATE_PRODUCT,
product,
)
ctx.JSON(http.StatusOK, res)

}

// Delete implements ProductController.
func (p *productController) Delete(ctx *gin.Context) {
panic("unimplemented")
}

// GetAll implements ProductController.
func (p *productController) GetAll(ctx *gin.Context) {
products, err := p.productService.GetAll(ctx.Request.Context())
if err != nil {
res := utils.BuildResponseFailed(
dto.MESSAGE_FAILED_GET_LIST_PRODUCT,
err.Error(),
nil,
)
ctx.JSON(http.StatusInternalServerError, res)
return
}

res := utils.BuildResponseSuccess(
dto.MESSAGE_SUCCESS_GET_LIST_PRODUCT,
products,
)
ctx.JSON(http.StatusOK, res)
}

// GetById implements ProductController.
func (p *productController) GetById(ctx *gin.Context) {
panic("unimplemented")
}

// Update implements ProductController.
func (p *productController) Update(ctx *gin.Context) {
panic("unimplemented")
}

func NewProductController(injector *do.Injector, s service.ProductService) ProductController {
db := do.MustInvokeNamed[*gorm.DB](injector, constants.DB)
productValidation := validation.NewProductValidation()
return &productController{
Expand Down
20 changes: 14 additions & 6 deletions modules/product/routes.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package products

import (
// "github.com/ofrendialsa/neromerce/modules/product/controller"
"github.com/gin-gonic/gin"
"github.com/ofrendialsa/neromerce/middlewares"
"github.com/ofrendialsa/neromerce/modules/auth/service"
"github.com/ofrendialsa/neromerce/modules/product/controller"
"github.com/ofrendialsa/neromerce/pkg/constants"
"github.com/samber/do"
)

func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
// productsController := do.MustInvoke[controller.ProductsController](injector)
productController := do.MustInvoke[controller.ProductController](injector)
jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService)
authAccess := middlewares.Authenticate(jwtService)
roleAccess := middlewares.Authorize("admin")

// productsRoutes := server.Group("/api/products")
// {
// // TODO: add your endpoints here
// }
productRoutes := server.Group("/api/products")
{
productRoutes.GET("", productController.GetAll)
productRoutes.POST("", authAccess, roleAccess, productController.Create)
// productRoutes.DELETE("/:id", authAccess, roleAccess, productController.Delete)
}
}
20 changes: 10 additions & 10 deletions modules/product/service/product_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import (
)

type ProductService interface {
CreateProduct(ctx context.Context, req dto.ProductCreateRequest) (dto.ProductResponse, error)
GetAllProducts(ctx context.Context) ([]dto.ProductResponse, error)
GetProductByID(ctx context.Context, productId uuid.UUID) (dto.ProductResponse, error)
UpdateProduct(ctx context.Context, req dto.ProductUpdateRequest, productId uuid.UUID) (dto.ProductResponse, error)
DeleteProduct(ctx context.Context, productId uuid.UUID) error
Create(ctx context.Context, req dto.ProductCreateRequest) (dto.ProductResponse, error)
GetAll(ctx context.Context) ([]dto.ProductResponse, error)
GetByID(ctx context.Context, productId uuid.UUID) (dto.ProductResponse, error)
Update(ctx context.Context, req dto.ProductUpdateRequest, productId uuid.UUID) (dto.ProductResponse, error)
Delete(ctx context.Context, productId uuid.UUID) error
}

type productService struct {
Expand All @@ -25,7 +25,7 @@ type productService struct {
}

// CreateProduct implements ProductService.
func (p *productService) CreateProduct(ctx context.Context, req dto.ProductCreateRequest) (dto.ProductResponse, error) {
func (p *productService) Create(ctx context.Context, req dto.ProductCreateRequest) (dto.ProductResponse, error) {
product := entities.Product{
Name: req.Name,
Description: req.Description,
Expand All @@ -44,7 +44,7 @@ func (p *productService) CreateProduct(ctx context.Context, req dto.ProductCreat
}

// UpdateProduct implements ProductService.
func (p *productService) UpdateProduct(ctx context.Context, req dto.ProductUpdateRequest, productId uuid.UUID) (dto.ProductResponse, error) {
func (p *productService) Update(ctx context.Context, req dto.ProductUpdateRequest, productId uuid.UUID) (dto.ProductResponse, error) {

updates := map[string]interface{}{}

Expand Down Expand Up @@ -80,7 +80,7 @@ func (p *productService) UpdateProduct(ctx context.Context, req dto.ProductUpdat
}

// GetAllProducts implements ProductService.
func (p *productService) GetAllProducts(ctx context.Context) ([]dto.ProductResponse, error) {
func (p *productService) GetAll(ctx context.Context) ([]dto.ProductResponse, error) {
products, err := p.productRepository.GetAllProducts(ctx, p.db)
if err != nil {
return nil, err
Expand All @@ -102,7 +102,7 @@ func (p *productService) GetAllProducts(ctx context.Context) ([]dto.ProductRespo
}

// GetProductByID implements ProductService.
func (p *productService) GetProductByID(ctx context.Context, productId uuid.UUID) (dto.ProductResponse, error) {
func (p *productService) GetByID(ctx context.Context, productId uuid.UUID) (dto.ProductResponse, error) {
product, err := p.productRepository.GetProductByID(ctx, p.db, productId)
if err != nil {
return dto.ProductResponse{}, err
Expand All @@ -112,7 +112,7 @@ func (p *productService) GetProductByID(ctx context.Context, productId uuid.UUID
}

// DeleteProduct implements ProductService.
func (p *productService) DeleteProduct(ctx context.Context, productId uuid.UUID) error {
func (p *productService) Delete(ctx context.Context, productId uuid.UUID) error {
return p.productRepository.DeleteProduct(ctx, p.db, productId)
}

Expand Down
18 changes: 9 additions & 9 deletions modules/product/tests/product_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestGetProductByID(t *testing.T) {
}
mockRepo.On("GetProductByID", ctx, mock.Anything, productID).Return(expectedProduct, nil).Once()

result, err := service.GetProductByID(ctx, productID)
result, err := service.GetByID(ctx, productID)

assert.NoError(t, err)
assert.Equal(t, expectedProduct.Name, result.Name)
Expand All @@ -39,7 +39,7 @@ func TestGetProductByID(t *testing.T) {
t.Run("Error Not Found", func(t *testing.T) {
mockRepo.On("GetProductByID", ctx, mock.Anything, productID).Return(entities.Product{}, errors.New("not found")).Once()

result, err := service.GetProductByID(ctx, productID)
result, err := service.GetByID(ctx, productID)

assert.Error(t, err)
assert.Empty(t, result.Name)
Expand All @@ -61,7 +61,7 @@ func TestGetAllProducts(t *testing.T) {

mockRepo.On("GetAllProducts", ctx, mock.Anything).Return(expectedEntities, nil).Once()

result, err := service.GetAllProducts(ctx)
result, err := service.GetAll(ctx)

assert.NoError(t, err)
assert.Len(t, result, 2)
Expand All @@ -73,7 +73,7 @@ func TestGetAllProducts(t *testing.T) {
t.Run("Error Database", func(t *testing.T) {
mockRepo.On("GetAllProducts", ctx, mock.Anything).Return([]entities.Product{}, errors.New("db error")).Once()

result, err := service.GetAllProducts(ctx)
result, err := service.GetAll(ctx)

assert.Error(t, err)
assert.Nil(t, result)
Expand Down Expand Up @@ -105,7 +105,7 @@ func TestCreateProduct(t *testing.T) {
return p.Name == req.Name && p.Price == req.Price
})).Return(expectedEntity, nil).Once()

result, err := service.CreateProduct(ctx, req)
result, err := service.Create(ctx, req)

assert.NoError(t, err)
assert.Equal(t, productID.String(), result.ID)
Expand All @@ -123,7 +123,7 @@ func TestDeleteProduct(t *testing.T) {
t.Run("Success", func(t *testing.T) {
mockRepo.On("DeleteProduct", ctx, mock.Anything, productID).Return(nil).Once()

err := service.DeleteProduct(ctx, productID)
err := service.Delete(ctx, productID)

assert.NoError(t, err)
mockRepo.AssertExpectations(t)
Expand All @@ -132,7 +132,7 @@ func TestDeleteProduct(t *testing.T) {
t.Run("Failed", func(t *testing.T) {
mockRepo.On("DeleteProduct", ctx, mock.Anything, productID).Return(errors.New("failed to delete")).Once()

err := service.DeleteProduct(ctx, productID)
err := service.Delete(ctx, productID)

assert.Error(t, err)
assert.Equal(t, "failed to delete", err.Error())
Expand Down Expand Up @@ -178,7 +178,7 @@ func TestUpdateProduct(t *testing.T) {
Return(expectedEntity, nil).
Once()

result, err := svc.UpdateProduct(ctx, req, productID)
result, err := svc.Update(ctx, req, productID)

assert.NoError(t, err)
assert.Equal(t, productID.String(), result.ID)
Expand All @@ -199,7 +199,7 @@ func TestUpdateProduct(t *testing.T) {
Return(entities.Product{}, errors.New("update failed")).
Once()

result, err := svc.UpdateProduct(ctx, req, productID)
result, err := svc.Update(ctx, req, productID)

assert.Error(t, err)
assert.Empty(t, result.ID)
Expand Down
11 changes: 11 additions & 0 deletions providers/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
categoryController "github.com/ofrendialsa/neromerce/modules/category/controller"
categoryRepo "github.com/ofrendialsa/neromerce/modules/category/repository"
categoryService "github.com/ofrendialsa/neromerce/modules/category/service"
productController "github.com/ofrendialsa/neromerce/modules/product/controller"
productRepo "github.com/ofrendialsa/neromerce/modules/product/repository"
productService "github.com/ofrendialsa/neromerce/modules/product/service"
userController "github.com/ofrendialsa/neromerce/modules/user/controller"
"github.com/ofrendialsa/neromerce/modules/user/repository"
userService "github.com/ofrendialsa/neromerce/modules/user/service"
Expand Down Expand Up @@ -35,10 +38,12 @@ func RegisterDependencies(injector *do.Injector) {
userRepository := repository.NewUserRepository(db)
refreshTokenRepository := authRepo.NewRefreshTokenRepository(db)
categoryRepository := categoryRepo.NewCategoryRepository(db)
productRepository := productRepo.NewProductRepository(db)

userService := userService.NewUserService(userRepository, db)
authService := authService.NewAuthService(userRepository, refreshTokenRepository, jwtService, db)
categoryService := categoryService.NewCategoryService(categoryRepository)
productService := productService.NewProductService(productRepository, db)

do.Provide(
injector, func(i *do.Injector) (userController.UserController, error) {
Expand All @@ -57,4 +62,10 @@ func RegisterDependencies(injector *do.Injector) {
return categoryController.NewCategoryController(categoryService), nil
},
)

do.Provide(
injector, func(i *do.Injector) (productController.ProductController, error) {
return productController.NewProductController(i, productService), nil
},
)
}