346 lines
10 KiB
Go
346 lines
10 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/oklog/ulid/v2"
|
|
"github.com/timetracker/backend/internal/api/utils"
|
|
dto "github.com/timetracker/backend/internal/dtos"
|
|
"github.com/timetracker/backend/internal/models"
|
|
)
|
|
|
|
// 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) {
|
|
// Get projects from the database
|
|
projects, err := models.GetAllProjects(c.Request.Context())
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error retrieving projects: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Convert to DTOs
|
|
projectDTOs := make([]dto.ProjectDto, len(projects))
|
|
for i, project := range projects {
|
|
projectDTOs[i] = convertProjectToDTO(&project)
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, projectDTOs)
|
|
}
|
|
|
|
// 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) {
|
|
// Get projects with customers from the database
|
|
projects, err := models.GetAllProjectsWithCustomers(c.Request.Context())
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error retrieving projects: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Convert to DTOs
|
|
projectDTOs := make([]dto.ProjectDto, len(projects))
|
|
for i, project := range projects {
|
|
projectDTOs[i] = convertProjectToDTO(&project)
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, projectDTOs)
|
|
}
|
|
|
|
// 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) {
|
|
// Parse ID from URL
|
|
idStr := c.Param("id")
|
|
id, err := ulid.Parse(idStr)
|
|
if err != nil {
|
|
utils.BadRequestResponse(c, "Invalid project ID format")
|
|
return
|
|
}
|
|
|
|
// Get project from the database
|
|
project, err := models.GetProjectByID(c.Request.Context(), models.FromULID(id))
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error retrieving project: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if project == nil {
|
|
utils.NotFoundResponse(c, "Project not found")
|
|
return
|
|
}
|
|
|
|
// Convert to DTO
|
|
projectDTO := convertProjectToDTO(project)
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, projectDTO)
|
|
}
|
|
|
|
// 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) {
|
|
// Parse customer ID from URL
|
|
customerIDStr := c.Param("customerId")
|
|
customerID, err := ulid.Parse(customerIDStr)
|
|
if err != nil {
|
|
utils.BadRequestResponse(c, "Invalid customer ID format")
|
|
return
|
|
}
|
|
|
|
// Get projects from the database
|
|
projects, err := models.GetProjectsByCustomerID(c.Request.Context(), customerID)
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error retrieving projects: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Convert to DTOs
|
|
projectDTOs := make([]dto.ProjectDto, len(projects))
|
|
for i, project := range projects {
|
|
projectDTOs[i] = convertProjectToDTO(&project)
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, projectDTOs)
|
|
}
|
|
|
|
// 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) {
|
|
// Parse request body
|
|
var projectCreateDTO dto.ProjectCreateDto
|
|
if err := c.ShouldBindJSON(&projectCreateDTO); err != nil {
|
|
utils.BadRequestResponse(c, "Invalid request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Convert DTO to model
|
|
projectCreate, err := convertCreateProjectDTOToModel(projectCreateDTO)
|
|
if err != nil {
|
|
utils.BadRequestResponse(c, err.Error())
|
|
return
|
|
}
|
|
|
|
// Create project in the database
|
|
project, err := models.CreateProject(c.Request.Context(), projectCreate)
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error creating project: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Convert to DTO
|
|
projectDTO := convertProjectToDTO(project)
|
|
|
|
utils.SuccessResponse(c, http.StatusCreated, projectDTO)
|
|
}
|
|
|
|
// 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) {
|
|
// Parse ID from URL
|
|
idStr := c.Param("id")
|
|
id, err := ulid.Parse(idStr)
|
|
if err != nil {
|
|
utils.BadRequestResponse(c, "Invalid project ID format")
|
|
return
|
|
}
|
|
|
|
// Parse request body
|
|
var projectUpdateDTO dto.ProjectUpdateDto
|
|
if err := c.ShouldBindJSON(&projectUpdateDTO); err != nil {
|
|
utils.BadRequestResponse(c, "Invalid request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Set ID from URL
|
|
projectUpdateDTO.ID = id.String()
|
|
|
|
// Convert DTO to model
|
|
projectUpdate, err := convertUpdateProjectDTOToModel(projectUpdateDTO)
|
|
if err != nil {
|
|
utils.BadRequestResponse(c, err.Error())
|
|
return
|
|
}
|
|
|
|
// Update project in the database
|
|
project, err := models.UpdateProject(c.Request.Context(), projectUpdate)
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error updating project: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if project == nil {
|
|
utils.NotFoundResponse(c, "Project not found")
|
|
return
|
|
}
|
|
|
|
// Convert to DTO
|
|
projectDTO := convertProjectToDTO(project)
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, projectDTO)
|
|
}
|
|
|
|
// 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) {
|
|
// Parse ID from URL
|
|
idStr := c.Param("id")
|
|
id, err := ulid.Parse(idStr)
|
|
if err != nil {
|
|
utils.BadRequestResponse(c, "Invalid project ID format")
|
|
return
|
|
}
|
|
|
|
// Delete project from the database
|
|
err = models.DeleteProject(c.Request.Context(), id)
|
|
if err != nil {
|
|
utils.InternalErrorResponse(c, "Error deleting project: "+err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// Helper functions for DTO conversion
|
|
|
|
func convertProjectToDTO(project *models.Project) dto.ProjectDto {
|
|
|
|
return dto.ProjectDto{
|
|
ID: project.ID.String(),
|
|
CreatedAt: project.CreatedAt,
|
|
UpdatedAt: project.UpdatedAt,
|
|
Name: project.Name,
|
|
CustomerID: project.CustomerID.String(),
|
|
}
|
|
}
|
|
|
|
func convertCreateProjectDTOToModel(dto dto.ProjectCreateDto) (models.ProjectCreate, error) {
|
|
// Convert CustomerID from int to ULID (this is a simplification, adjust as needed)
|
|
customerID, err := models.ULIDWrapperFromString(dto.CustomerID)
|
|
if err != nil {
|
|
return models.ProjectCreate{}, fmt.Errorf("invalid customer ID: %w", err)
|
|
}
|
|
|
|
return models.ProjectCreate{
|
|
Name: dto.Name,
|
|
CustomerID: customerID,
|
|
}, nil
|
|
}
|
|
|
|
func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto) (models.ProjectUpdate, error) {
|
|
id, err := ulid.Parse(dto.ID)
|
|
if err != nil {
|
|
return models.ProjectUpdate{}, fmt.Errorf("invalid project ID: %w", err)
|
|
}
|
|
update := models.ProjectUpdate{
|
|
ID: models.FromULID(id),
|
|
}
|
|
|
|
if dto.Name != nil {
|
|
update.Name = dto.Name
|
|
}
|
|
|
|
if dto.CustomerID != nil {
|
|
// Convert CustomerID from int to ULID (this is a simplification, adjust as needed)
|
|
customerID, err := models.ULIDWrapperFromString(*dto.CustomerID)
|
|
if err != nil {
|
|
return models.ProjectUpdate{}, fmt.Errorf("invalid customer ID: %w", err)
|
|
}
|
|
update.CustomerID = &customerID
|
|
}
|
|
|
|
return update, nil
|
|
}
|