completed go backend

This commit is contained in:
2025-01-03 16:18:25 +00:00
parent f151fa7eae
commit aca98554d0
24 changed files with 610 additions and 150 deletions
@@ -1,21 +1,37 @@
package services
import (
"actatempus_backend/internal/domain/app_error"
"net/http"
"github.com/gin-gonic/gin"
)
// HandleError handles errors by formatting them as JSON.
func HandleError(c *gin.Context) func(error) any {
return func(err error) any {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return nil
// Check if the error is of type *AppError
if appErr, ok := err.(*app_error.AppError); ok {
// Use the AppError fields for the JSON response
c.JSON(appErr.Status, gin.H{
"code": appErr.Code,
"message": appErr.Message,
"details": appErr.Err.Error(), // Original error if available
})
} else {
// Fallback for non-AppError errors
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
return nil
}
}
func HandleSuccess[T any](c *gin.Context, statusCode int) func(T) any {
return func(data T) any {
c.JSON(statusCode, data)
return nil
c.JSON(statusCode, data)
return nil
}
}
}
@@ -0,0 +1,109 @@
package services
import (
"actatempus_backend/internal/application/services/dto"
mappers "actatempus_backend/internal/application/services/mapper"
"actatempus_backend/internal/domain/repository"
"net/http"
A "github.com/IBM/fp-go/array"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/gin-gonic/gin"
)
// ProjectService handles project-related HTTP requests.
type ProjectService struct {
repository repository.ProjectRepository
}
// NewProjectService creates a new instance of ProjectService.
func NewProjectService(repo repository.ProjectRepository) *ProjectService {
return &ProjectService{repository: repo}
}
// RegisterRoutes registers the project-related routes with Gin.
func (s *ProjectService) RegisterRoutes(router *gin.RouterGroup) {
router.POST("/", s.CreateProject)
router.GET("/:id", s.GetProjectByID)
router.GET("/", s.GetAllProjects)
router.PUT("/:id", s.UpdateProject)
router.DELETE("/:id", s.DeleteProject)
}
// CreateProject handles the creation of a new project.
func (s *ProjectService) CreateProject(c *gin.Context) {
var projectCreateDTO dto.ProjectCreateDTO
if err := c.ShouldBindJSON(&projectCreateDTO); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
F.Pipe3(
mappers.MapCreateDTOToProject(projectCreateDTO),
s.repository.Create(c.Request.Context()),
E.Map[error](mappers.MapProjectToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectDTO](c, http.StatusCreated),
),
)
}
// GetProjectByID handles fetching a project by its ID.
func (s *ProjectService) GetProjectByID(c *gin.Context) {
F.Pipe3(
c.Param("id"),
s.repository.FindByID(c.Request.Context()),
E.Map[error](mappers.MapProjectToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectDTO](c, http.StatusOK),
),
)
}
// GetAllProjects handles fetching all projects.
func (s *ProjectService) GetAllProjects(c *gin.Context) {
F.Pipe2(
s.repository.FindAll(c.Request.Context()),
E.Map[error](A.Map(mappers.MapProjectToDTO)),
E.Fold(
HandleError(c),
HandleSuccess[[]dto.ProjectDTO](c, http.StatusOK),
),
)
}
// UpdateProject handles updating an existing project.
func (s *ProjectService) UpdateProject(c *gin.Context) {
id := c.Param("id")
var projectUpdateDTO dto.ProjectUpdateDTO
if err := c.ShouldBindJSON(&projectUpdateDTO); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
F.Pipe3(
mappers.MapUpdateDTOToProject(projectUpdateDTO, id),
s.repository.Update(c.Request.Context()),
E.Map[error](mappers.MapProjectToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectDTO](c, http.StatusOK),
),
)
}
// DeleteProject handles deleting a project by its ID.
func (s *ProjectService) DeleteProject(c *gin.Context) {
F.Pipe3(
c.Param("id"),
s.repository.Delete(c.Request.Context()),
E.Map[error](mappers.MapProjectToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectDTO](c, http.StatusOK),
),
)
}
@@ -0,0 +1,123 @@
package services
import (
"actatempus_backend/internal/application/services/dto"
mappers "actatempus_backend/internal/application/services/mapper"
"actatempus_backend/internal/domain/repository"
"net/http"
A "github.com/IBM/fp-go/array"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/gin-gonic/gin"
)
// ProjectTaskService handles project task-related HTTP requests.
type ProjectTaskService struct {
repository repository.ProjectTaskRepository
}
// NewProjectTaskService creates a new instance of ProjectTaskService.
func NewProjectTaskService(repo repository.ProjectTaskRepository) *ProjectTaskService {
return &ProjectTaskService{repository: repo}
}
// RegisterRoutes registers the project task-related routes with Gin.
func (s *ProjectTaskService) RegisterRoutes(router *gin.RouterGroup) {
router.POST("/", s.CreateTask)
router.GET("/:id", s.GetTaskByID)
router.GET("/", s.GetAllTasks)
router.GET("/project/:projectID", s.GetTasksByProjectID)
router.PUT("/:id", s.UpdateTask)
router.DELETE("/:id", s.DeleteTask)
}
// CreateTask handles the creation of a new project task.
func (s *ProjectTaskService) CreateTask(c *gin.Context) {
var taskCreateDTO dto.ProjectTaskCreateDTO
if err := c.ShouldBindJSON(&taskCreateDTO); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
F.Pipe3(
mappers.MapCreateDTOToProjectTask(taskCreateDTO),
s.repository.Create(c.Request.Context()),
E.Map[error](mappers.MapProjectTaskToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectTaskDTO](c, http.StatusCreated),
),
)
}
// GetTaskByID handles fetching a project task by its ID.
func (s *ProjectTaskService) GetTaskByID(c *gin.Context) {
F.Pipe3(
c.Param("id"),
s.repository.FindByID(c.Request.Context()),
E.Map[error](mappers.MapProjectTaskToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectTaskDTO](c, http.StatusOK),
),
)
}
// GetTasksByProjectID handles fetching project tasks by project ID.
func (s *ProjectTaskService) GetTasksByProjectID(c *gin.Context) {
F.Pipe3(
c.Param("projectID"),
s.repository.FindByProjectID(c.Request.Context()),
E.Map[error](A.Map(mappers.MapProjectTaskToDTO)),
E.Fold(
HandleError(c),
HandleSuccess[[]dto.ProjectTaskDTO](c, http.StatusOK),
),
)
}
// GetAllTasks handles fetching all project tasks.
func (s *ProjectTaskService) GetAllTasks(c *gin.Context) {
F.Pipe2(
s.repository.FindAll(c.Request.Context()),
E.Map[error](A.Map(mappers.MapProjectTaskToDTO)),
E.Fold(
HandleError(c),
HandleSuccess[[]dto.ProjectTaskDTO](c, http.StatusOK),
),
)
}
// UpdateTask handles updating an existing project task.
func (s *ProjectTaskService) UpdateTask(c *gin.Context) {
id := c.Param("id")
var taskUpdateDTO dto.ProjectTaskUpdateDTO
if err := c.ShouldBindJSON(&taskUpdateDTO); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
F.Pipe3(
mappers.MapUpdateDTOToProjectTask(taskUpdateDTO, id),
s.repository.Update(c.Request.Context()),
E.Map[error](mappers.MapProjectTaskToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectTaskDTO](c, http.StatusOK),
),
)
}
// DeleteTask handles deleting a project task by its ID and returns the deleted object.
func (s *ProjectTaskService) DeleteTask(c *gin.Context) {
F.Pipe3(
c.Param("id"),
s.repository.Delete(c.Request.Context()),
E.Map[error](mappers.MapProjectTaskToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.ProjectTaskDTO](c, http.StatusOK),
),
)
}
@@ -0,0 +1,137 @@
package services
import (
"actatempus_backend/internal/application/services/dto"
mappers "actatempus_backend/internal/application/services/mapper"
"actatempus_backend/internal/domain/repository"
"net/http"
A "github.com/IBM/fp-go/array"
E "github.com/IBM/fp-go/either"
F "github.com/IBM/fp-go/function"
"github.com/gin-gonic/gin"
)
// TimeEntryService handles time entry-related HTTP requests.
type TimeEntryService struct {
repository repository.TimeEntryRepository
}
// NewTimeEntryService creates a new instance of TimeEntryService.
func NewTimeEntryService(repo repository.TimeEntryRepository) *TimeEntryService {
return &TimeEntryService{repository: repo}
}
// RegisterRoutes registers the time entry-related routes with Gin.
func (s *TimeEntryService) RegisterRoutes(router *gin.RouterGroup) {
router.POST("/", s.CreateTimeEntry)
router.GET("/:id", s.GetTimeEntryByID)
router.GET("/", s.GetAllTimeEntries)
router.GET("/user/:userID", s.GetTimeEntriesByUserID)
router.GET("/project/:projectID", s.GetTimeEntriesByProjectID)
router.PUT("/:id", s.UpdateTimeEntry)
router.DELETE("/:id", s.DeleteTimeEntry)
}
// CreateTimeEntry handles the creation of a new time entry.
func (s *TimeEntryService) CreateTimeEntry(c *gin.Context) {
var timeEntryCreateDTO dto.TimeEntryCreateDTO
if err := c.ShouldBindJSON(&timeEntryCreateDTO); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
F.Pipe3(
mappers.MapCreateDTOToTimeEntry(timeEntryCreateDTO),
s.repository.Create(c.Request.Context()),
E.Map[error](mappers.MapTimeEntryToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.TimeEntryDTO](c, http.StatusCreated),
),
)
}
// GetTimeEntryByID handles fetching a time entry by its ID.
func (s *TimeEntryService) GetTimeEntryByID(c *gin.Context) {
F.Pipe3(
c.Param("id"),
s.repository.FindByID(c.Request.Context()),
E.Map[error](mappers.MapTimeEntryToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.TimeEntryDTO](c, http.StatusOK),
),
)
}
// GetTimeEntriesByUserID handles fetching time entries by user ID.
func (s *TimeEntryService) GetTimeEntriesByUserID(c *gin.Context) {
F.Pipe3(
c.Param("userID"),
s.repository.FindByUserID(c.Request.Context()),
E.Map[error](A.Map(mappers.MapTimeEntryToDTO)),
E.Fold(
HandleError(c),
HandleSuccess[[]dto.TimeEntryDTO](c, http.StatusOK),
),
)
}
// GetTimeEntriesByProjectID handles fetching time entries by project ID.
func (s *TimeEntryService) GetTimeEntriesByProjectID(c *gin.Context) {
F.Pipe3(
c.Param("projectID"),
s.repository.FindByProjectID(c.Request.Context()),
E.Map[error](A.Map(mappers.MapTimeEntryToDTO)),
E.Fold(
HandleError(c),
HandleSuccess[[]dto.TimeEntryDTO](c, http.StatusOK),
),
)
}
// GetAllTimeEntries handles fetching all time entries.
func (s *TimeEntryService) GetAllTimeEntries(c *gin.Context) {
F.Pipe2(
s.repository.FindAll(c.Request.Context()),
E.Map[error](A.Map(mappers.MapTimeEntryToDTO)),
E.Fold(
HandleError(c),
HandleSuccess[[]dto.TimeEntryDTO](c, http.StatusOK),
),
)
}
// UpdateTimeEntry handles updating an existing time entry.
func (s *TimeEntryService) UpdateTimeEntry(c *gin.Context) {
id := c.Param("id")
var timeEntryUpdateDTO dto.TimeEntryUpdateDTO
if err := c.ShouldBindJSON(&timeEntryUpdateDTO); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
F.Pipe3(
mappers.MapUpdateDTOToTimeEntry(timeEntryUpdateDTO, id),
s.repository.Update(c.Request.Context()),
E.Map[error](mappers.MapTimeEntryToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.TimeEntryDTO](c, http.StatusOK),
),
)
}
// DeleteTimeEntry handles deleting a time entry by its ID and returns the deleted object.
func (s *TimeEntryService) DeleteTimeEntry(c *gin.Context) {
F.Pipe3(
c.Param("id"),
s.repository.Delete(c.Request.Context()),
E.Map[error](mappers.MapTimeEntryToDTO),
E.Fold(
HandleError(c),
HandleSuccess[dto.TimeEntryDTO](c, http.StatusOK),
),
)
}