Commit 260f1234 by mhmdhaekal

feat(initialize): Implement handler

1 parent a0e78e30
......@@ -4,6 +4,7 @@ import (
"log"
"os"
"github.com/gofiber/fiber/v3"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"ui.ac.id/dtd/go-webserver-boilerplate/internal/handler"
......@@ -21,5 +22,20 @@ func main() {
userRepository := postgresql.NewPostgresUserRepository(db)
userService := service.NewUserService(userRepository)
_ = handler.NewUserHandler(userService)
userHandler := handler.NewUserHandler(userService)
app := fiber.New(fiber.Config{
ErrorHandler: handler.ErrorHandler,
})
//api group
api := app.Group("/api")
//v1 api
v1 := api.Group("/v1")
//user group
user := v1.Group("/user")
user.Get("/:id", userHandler.GetUserDetail)
user.Get("/", userHandler.GetUserList)
}
package domain
import (
"errors"
"fmt"
)
type ErrorCode struct {
slug string
}
func (e ErrorCode) String() string {
return e.slug
}
var (
Unknown = ErrorCode{"unknown"}
NotFound = ErrorCode{"not_found"}
Validation = ErrorCode{"validation_error"}
Database = ErrorCode{"database_error"}
InactiveUser = ErrorCode{"user_inactive"}
)
func FromString(s string) (ErrorCode, error) {
switch s {
case NotFound.slug:
return NotFound, nil
case Validation.slug:
return Validation, nil
case Database.slug:
return Database, nil
case InactiveUser.slug:
return InactiveUser, nil
}
return Unknown, errors.New("unknown error code: " + s)
}
type Error struct {
Code ErrorCode
Message string
Err error
}
func (e *Error) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Message, e.Err)
}
return e.Message
}
func (e *Error) Unwrap() error {
return e.Err
}
var (
ErrNotFound = &Error{
Code: NotFound,
Message: "resource not found",
}
ErrValidation = &Error{
Code: Validation,
Message: "validation failed",
}
ErrDatabase = &Error{
Code: Database,
Message: "database operation failed",
}
ErrInactiveUser = &Error{
Code: InactiveUser,
Message: "user account is inactive",
}
)
func NewError(code ErrorCode, message string, err error) *Error {
return &Error{
Code: code,
Message: message,
Err: err,
}
}
......@@ -9,6 +9,8 @@ type User struct {
}
type UserService interface {
GetUserDetail(id int) (*User, error)
GetList() ([]User, error)
}
type UserRepository interface {
......
package handler
type BaseResponse struct {
Success bool `json:"success"`
Code int `json:"code"`
Data interface{} `json:"data,omitempty"`
Error interface{} `json:"error,omitempty"`
}
type ErrorResponse struct {
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
func NewBaseResponse(code int, data interface{}, err error) BaseResponse {
if err != nil {
return BaseResponse{
Success: false,
Code: code,
Error: formatError(err),
}
}
return BaseResponse{
Success: true,
Code: code,
Data: data,
}
}
package handler
import (
"github.com/gofiber/fiber/v3"
"ui.ac.id/dtd/go-webserver-boilerplate/internal/domain" // Adjust import path to your project
)
func ErrorHandler(ctx fiber.Ctx, err error) error {
if err == nil {
return nil
}
code := fiber.StatusInternalServerError
if customErr, ok := err.(*domain.Error); ok {
switch customErr.Code {
case domain.NotFound:
code = fiber.StatusNotFound
case domain.Validation:
code = fiber.StatusBadRequest
case domain.Database:
code = fiber.StatusInternalServerError
case domain.InactiveUser:
code = fiber.StatusForbidden
default:
code = fiber.StatusInternalServerError
}
} else if fiberErr, ok := err.(*fiber.Error); ok {
code = fiberErr.Code
}
response := NewBaseResponse(code, nil, err)
return ctx.Status(code).JSON(response)
}
func formatError(err error) ErrorResponse {
if customErr, ok := err.(*domain.Error); ok {
errResp := ErrorResponse{
Message: customErr.Message,
}
if customErr.Err != nil {
errResp.Details = customErr.Err.Error()
}
return errResp
}
return ErrorResponse{
Message: err.Error(),
}
}
package specification
import "ui.ac.id/dtd/go-webserver-boilerplate/internal/domain"
type UserCreate struct {
Name string `json:"name"`
Email string `json:"email"`
}
type UserDetailResponse struct {
User domain.User `json:"user"`
}
type UserListResponse struct {
Users []domain.User `json:"users"`
}
package handler
import (
"strconv"
"github.com/gofiber/fiber/v3"
"ui.ac.id/dtd/go-webserver-boilerplate/internal/domain"
"ui.ac.id/dtd/go-webserver-boilerplate/internal/handler/specification"
)
type UserHandler struct {
......@@ -15,6 +18,26 @@ func NewUserHandler(service domain.UserService) *UserHandler {
}
}
func (h *UserHandler) GetUser(ctx *fiber.Ctx) {
func (h *UserHandler) GetUserList(ctx fiber.Ctx) error {
users, err := h.service.GetList()
if err != nil {
return err
}
response := NewBaseResponse(200, specification.UserListResponse{Users: users}, nil)
return ctx.JSON(response)
}
func (h *UserHandler) GetUserDetail(ctx fiber.Ctx) error {
userIDParams := ctx.Params("id")
userID, err := strconv.Atoi(userIDParams)
user, err := h.service.GetUserDetail(userID)
if err != nil {
return err
}
response := NewBaseResponse(200, specification.UserDetailResponse{User: *user}, nil)
return ctx.JSON(response)
}
package postgresql
import (
"errors"
"gorm.io/gorm"
"ui.ac.id/dtd/go-webserver-boilerplate/internal/domain"
)
func WrapGormError(err error) error {
if err == nil {
return nil
}
switch {
case errors.Is(err, gorm.ErrRecordNotFound):
return domain.ErrNotFound
default:
return domain.NewError(domain.Database, "Database error", err)
}
}
......@@ -21,7 +21,7 @@ func (repo *PostgresUserRepository) GetUserByID(id int) (*domain.User, error) {
result := repo.db.First(&user, id)
if result.Error != nil {
return nil, result.Error
return nil, WrapGormError(result.Error)
}
user_domain := user.ToDomain()
......
......@@ -11,3 +11,13 @@ func NewUserService(userRepository domain.UserRepository) *UserService {
userRepository: userRepository,
}
}
func (s *UserService) GetUserDetail(id int) (*domain.User, error) {
result, err := s.userRepository.GetUserByID(id)
return result, err
}
func (s *UserService) GetList() ([]domain.User, error) {
result, err := s.userRepository.GetList()
return result, err
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!