235 lines
7.9 KiB
Go

package handlers
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/timetracker/backend/internal/api/dto"
"github.com/timetracker/backend/internal/api/responses"
"github.com/timetracker/backend/internal/api/utils"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// ProjectHandler handles project-related API endpoints
type ProjectHandler struct{}
// NewProjectHandler creates a new ProjectHandler
func NewProjectHandler() *ProjectHandler {
return &ProjectHandler{}
}
// GetProjects handles GET /projects
//
// @Summary Get all projects
// @Description Get a list of all projects
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} utils.Response{data=[]dto.ProjectDto}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects [get]
func (h *ProjectHandler) GetProjects(c *gin.Context) {
utils.HandleGetAll(c, models.GetAllProjects, convertProjectToDTO, "projects")
}
// GetProjectsWithCustomers handles GET /projects/with-customers
//
// @Summary Get all projects with customer information
// @Description Get a list of all projects with their associated customer information
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} utils.Response{data=[]dto.ProjectDto}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects/with-customers [get]
func (h *ProjectHandler) GetProjectsWithCustomers(c *gin.Context) {
utils.HandleGetAll(c, models.GetAllProjectsWithCustomers, convertProjectToDTO, "projects with customers")
}
// GetProjectByID handles GET /projects/:id
//
// @Summary Get project by ID
// @Description Get a project by its ID
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path string true "Project ID"
// @Success 200 {object} utils.Response{data=dto.ProjectDto}
// @Failure 400 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 404 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects/{id} [get]
func (h *ProjectHandler) GetProjectByID(c *gin.Context) {
utils.HandleGetByID(c, models.GetProjectByID, convertProjectToDTO, "project")
}
// GetProjectsByCustomerID handles GET /projects/customer/:customerId
//
// @Summary Get projects by customer ID
// @Description Get a list of projects for a specific customer
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param customerId path string true "Customer ID"
// @Success 200 {object} utils.Response{data=[]dto.ProjectDto}
// @Failure 400 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects/customer/{customerId} [get]
func (h *ProjectHandler) GetProjectsByCustomerID(c *gin.Context) {
utils.HandleGetByFilter(c, models.GetProjectsByCustomerID, convertProjectToDTO, "projects", "customerId")
}
// CreateProject handles POST /projects
//
// @Summary Create a new project
// @Description Create a new project
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param project body dto.ProjectCreateDto true "Project data"
// @Success 201 {object} utils.Response{data=dto.ProjectDto}
// @Failure 400 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects [post]
func (h *ProjectHandler) CreateProject(c *gin.Context) {
utils.HandleCreate(c, createProjectWrapper, convertProjectToDTO, "project")
}
// UpdateProject handles PUT /projects/:id
//
// @Summary Update a project
// @Description Update an existing project
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path string true "Project ID"
// @Param project body dto.ProjectUpdateDto true "Project data"
// @Success 200 {object} utils.Response{data=dto.ProjectDto}
// @Failure 400 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 404 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects/{id} [put]
func (h *ProjectHandler) UpdateProject(c *gin.Context) {
utils.HandleUpdate(c, models.UpdateProject, convertProjectToDTO, prepareProjectUpdate, "project")
}
// DeleteProject handles DELETE /projects/:id
//
// @Summary Delete a project
// @Description Delete a project by its ID
// @Tags projects
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path string true "Project ID"
// @Success 204 {object} utils.Response
// @Failure 400 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 401 {object} utils.Response{error=utils.ErrorInfo}
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /projects/{id} [delete]
func (h *ProjectHandler) DeleteProject(c *gin.Context) {
utils.HandleDelete(c, models.DeleteProject, "project")
}
// Helper functions for DTO conversion
func convertProjectToDTO(project *models.Project) dto.ProjectDto {
customerId := project.CustomerID.String()
return dto.ProjectDto{
ID: project.ID.String(),
CreatedAt: project.CreatedAt,
UpdatedAt: project.UpdatedAt,
Name: project.Name,
CustomerID: &customerId,
}
}
func convertCreateProjectDTOToModel(dto dto.ProjectCreateDto) (models.ProjectCreate, error) {
create := models.ProjectCreate{Name: dto.Name}
// Convert CustomerID from int to ULID (this is a simplification, adjust as needed)
if dto.CustomerID != nil {
customerID, err := types.ULIDFromString(*dto.CustomerID)
if err != nil {
return models.ProjectCreate{}, fmt.Errorf("invalid customer ID: %w", err)
}
create.CustomerID = &customerID
}
return create, nil
}
func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto, id types.ULID) (models.ProjectUpdate, error) {
update := models.ProjectUpdate{
ID: id,
}
if dto.Name != nil {
update.Name = dto.Name
}
if dto.CustomerID.Valid {
if dto.CustomerID.Value == nil {
update.CustomerID = nil
} else {
customerID, err := types.ULIDFromString(*dto.CustomerID.Value)
if err != nil {
return models.ProjectUpdate{}, fmt.Errorf("invalid customer ID: %w", err)
}
update.CustomerID = &customerID
}
}
return update, nil
}
// prepareProjectUpdate prepares the project update object by parsing the ID, binding the JSON, and converting the DTO to a model
func prepareProjectUpdate(c *gin.Context) (models.ProjectUpdate, error) {
// Parse ID from URL
id, err := utils.ParseID(c, "id")
if err != nil {
responses.BadRequestResponse(c, "Invalid project ID format")
return models.ProjectUpdate{}, err
}
// Parse request body
var projectUpdateDTO dto.ProjectUpdateDto
if err := utils.BindJSON(c, &projectUpdateDTO); err != nil {
responses.BadRequestResponse(c, err.Error())
return models.ProjectUpdate{}, err
}
// Convert DTO to model
projectUpdate, err := convertUpdateProjectDTOToModel(projectUpdateDTO, id)
if err != nil {
responses.BadRequestResponse(c, err.Error())
return models.ProjectUpdate{}, err
}
return projectUpdate, nil
}
// createProjectWrapper is a wrapper function for models.CreateProject that takes a DTO as input
func createProjectWrapper(ctx context.Context, createDTO dto.ProjectCreateDto) (*models.Project, error) {
// Convert DTO to model
projectCreate, err := convertCreateProjectDTOToModel(createDTO)
if err != nil {
return nil, err
}
// Call the original function
return models.CreateProject(ctx, projectCreate)
}