feat: Refactor DTOs to use types.ULID and update companyId fields to be optional

This commit is contained in:
2025-03-12 09:32:29 +00:00
parent 233f3cdb5c
commit 4170eb5fbd
21 changed files with 269 additions and 264 deletions
@@ -8,6 +8,7 @@ import (
"github.com/timetracker/backend/internal/api/utils"
dto "github.com/timetracker/backend/internal/dtos"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// ActivityHandler handles activity-related API endpoints
@@ -72,7 +73,7 @@ func (h *ActivityHandler) GetActivityByID(c *gin.Context) {
}
// Get activity from the database
activity, err := models.GetActivityByID(c.Request.Context(), models.FromULID(id))
activity, err := models.GetActivityByID(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving activity: "+err.Error())
return
@@ -207,7 +208,7 @@ func (h *ActivityHandler) DeleteActivity(c *gin.Context) {
}
// Delete activity from the database
err = models.DeleteActivity(c.Request.Context(), models.FromULID(id))
err = models.DeleteActivity(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error deleting activity: "+err.Error())
return
@@ -238,7 +239,7 @@ func convertCreateActivityDTOToModel(dto dto.ActivityCreateDto) models.ActivityC
func convertUpdateActivityDTOToModel(dto dto.ActivityUpdateDto) models.ActivityUpdate {
id, _ := ulid.Parse(dto.ID)
update := models.ActivityUpdate{
ID: models.FromULID(id),
ID: types.FromULID(id),
}
if dto.Name != nil {
@@ -8,6 +8,7 @@ import (
"github.com/timetracker/backend/internal/api/utils"
dto "github.com/timetracker/backend/internal/dtos"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// CompanyHandler handles company-related API endpoints
@@ -72,7 +73,7 @@ func (h *CompanyHandler) GetCompanyByID(c *gin.Context) {
}
// Get company from the database
company, err := models.GetCompanyByID(c.Request.Context(), models.FromULID(id))
company, err := models.GetCompanyByID(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving company: "+err.Error())
return
@@ -207,7 +208,7 @@ func (h *CompanyHandler) DeleteCompany(c *gin.Context) {
}
// Delete company from the database
err = models.DeleteCompany(c.Request.Context(), models.FromULID(id))
err = models.DeleteCompany(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error deleting company: "+err.Error())
return
@@ -236,7 +237,7 @@ func convertCreateCompanyDTOToModel(dto dto.CompanyCreateDto) models.CompanyCrea
func convertUpdateCompanyDTOToModel(dto dto.CompanyUpdateDto) models.CompanyUpdate {
id, _ := ulid.Parse(dto.ID)
update := models.CompanyUpdate{
ID: models.FromULID(id),
ID: types.FromULID(id),
}
if dto.Name != nil {
@@ -6,9 +6,11 @@ import (
"github.com/gin-gonic/gin"
"github.com/oklog/ulid/v2"
"github.com/timetracker/backend/internal/api/middleware"
"github.com/timetracker/backend/internal/api/utils"
dto "github.com/timetracker/backend/internal/dtos"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// CustomerHandler handles customer-related API endpoints
@@ -73,7 +75,7 @@ func (h *CustomerHandler) GetCustomerByID(c *gin.Context) {
}
// Get customer from the database
customer, err := models.GetCustomerByID(c.Request.Context(), models.FromULID(id))
customer, err := models.GetCustomerByID(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving customer: "+err.Error())
return
@@ -144,6 +146,11 @@ func (h *CustomerHandler) GetCustomersByCompanyID(c *gin.Context) {
// @Failure 500 {object} utils.Response{error=utils.ErrorInfo}
// @Router /customers [post]
func (h *CustomerHandler) CreateCustomer(c *gin.Context) {
userID, err := middleware.GetUserIDFromContext(c)
if err != nil {
utils.UnauthorizedResponse(c, "User not authenticated")
return
}
// Parse request body
var customerCreateDTO dto.CustomerCreateDto
if err := c.ShouldBindJSON(&customerCreateDTO); err != nil {
@@ -157,6 +164,7 @@ func (h *CustomerHandler) CreateCustomer(c *gin.Context) {
utils.BadRequestResponse(c, "Invalid request body: "+err.Error())
return
}
customerCreate.OwnerUserID = &userID
// Create customer in the database
customer, err := models.CreateCustomer(c.Request.Context(), customerCreate)
@@ -255,7 +263,7 @@ func (h *CustomerHandler) DeleteCustomer(c *gin.Context) {
}
// Delete customer from the database
err = models.DeleteCustomer(c.Request.Context(), models.FromULID(id))
err = models.DeleteCustomer(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error deleting customer: "+err.Error())
return
@@ -267,22 +275,29 @@ func (h *CustomerHandler) DeleteCustomer(c *gin.Context) {
// Helper functions for DTO conversion
func convertCustomerToDTO(customer *models.Customer) dto.CustomerDto {
var companyID *string
if customer.CompanyID != nil {
s := customer.CompanyID.String()
companyID = &s
}
return dto.CustomerDto{
ID: customer.ID.String(),
CreatedAt: customer.CreatedAt,
UpdatedAt: customer.UpdatedAt,
Name: customer.Name,
CompanyID: customer.CompanyID.String(),
CompanyID: companyID,
}
}
func convertCreateCustomerDTOToModel(dto dto.CustomerCreateDto) (models.CustomerCreate, error) {
companyID, err := models.ULIDWrapperFromString(dto.CompanyID)
if err != nil {
return models.CustomerCreate{}, fmt.Errorf("invalid company ID: %w", err)
var companyID *types.ULID
if dto.CompanyID != nil {
wrapper, err := types.ULIDFromString(*dto.CompanyID) // Ignoring error, validation happens in the model
if err != nil {
return models.CustomerCreate{}, fmt.Errorf("invalid company ID: %w", err)
}
companyID = &wrapper
}
create := models.CustomerCreate{
Name: dto.Name,
CompanyID: companyID,
@@ -291,7 +306,7 @@ func convertCreateCustomerDTOToModel(dto dto.CustomerCreateDto) (models.Customer
}
func convertUpdateCustomerDTOToModel(dto dto.CustomerUpdateDto) (models.CustomerUpdate, error) {
id, err := models.ULIDWrapperFromString(dto.ID)
id, err := types.ULIDFromString(dto.ID)
if err != nil {
return models.CustomerUpdate{}, fmt.Errorf("invalid customer ID: %w", err)
}
@@ -305,11 +320,15 @@ func convertUpdateCustomerDTOToModel(dto dto.CustomerUpdateDto) (models.Customer
}
if dto.CompanyID != nil {
companyID, err := models.ULIDWrapperFromString(*dto.CompanyID)
if err != nil {
return models.CustomerUpdate{}, fmt.Errorf("invalid company ID: %w", err)
if dto.CompanyID.Valid {
companyID, err := types.ULIDFromString(*dto.CompanyID.Value)
if err != nil {
return models.CustomerUpdate{}, fmt.Errorf("invalid company ID: %w", err)
}
update.CompanyID = &companyID
} else {
update.CompanyID = nil
}
update.CompanyID = &companyID
}
return update, nil
@@ -9,6 +9,7 @@ import (
"github.com/timetracker/backend/internal/api/utils"
dto "github.com/timetracker/backend/internal/dtos"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// ProjectHandler handles project-related API endpoints
@@ -102,7 +103,7 @@ func (h *ProjectHandler) GetProjectByID(c *gin.Context) {
}
// Get project from the database
project, err := models.GetProjectByID(c.Request.Context(), models.FromULID(id))
project, err := models.GetProjectByID(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving project: "+err.Error())
return
@@ -308,7 +309,7 @@ func convertProjectToDTO(project *models.Project) dto.ProjectDto {
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)
customerID, err := types.ULIDFromString(dto.CustomerID)
if err != nil {
return models.ProjectCreate{}, fmt.Errorf("invalid customer ID: %w", err)
}
@@ -325,7 +326,7 @@ func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto) (models.ProjectUpd
return models.ProjectUpdate{}, fmt.Errorf("invalid project ID: %w", err)
}
update := models.ProjectUpdate{
ID: models.FromULID(id),
ID: types.FromULID(id),
}
if dto.Name != nil {
@@ -334,7 +335,7 @@ func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto) (models.ProjectUpd
if dto.CustomerID != nil {
// Convert CustomerID from int to ULID (this is a simplification, adjust as needed)
customerID, err := models.ULIDWrapperFromString(*dto.CustomerID)
customerID, err := types.ULIDFromString(*dto.CustomerID)
if err != nil {
return models.ProjectUpdate{}, fmt.Errorf("invalid customer ID: %w", err)
}
@@ -11,6 +11,7 @@ import (
"github.com/timetracker/backend/internal/api/utils"
dto "github.com/timetracker/backend/internal/dtos"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// TimeEntryHandler handles time entry-related API endpoints
@@ -75,7 +76,7 @@ func (h *TimeEntryHandler) GetTimeEntryByID(c *gin.Context) {
}
// Get time entry from the database
timeEntry, err := models.GetTimeEntryByID(c.Request.Context(), models.FromULID(id))
timeEntry, err := models.GetTimeEntryByID(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving time entry: "+err.Error())
return
@@ -116,7 +117,7 @@ func (h *TimeEntryHandler) GetTimeEntriesByUserID(c *gin.Context) {
}
// Get time entries from the database
timeEntries, err := models.GetTimeEntriesByUserID(c.Request.Context(), models.FromULID(userID))
timeEntries, err := models.GetTimeEntriesByUserID(c.Request.Context(), types.FromULID(userID))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving time entries: "+err.Error())
return
@@ -152,7 +153,7 @@ func (h *TimeEntryHandler) GetMyTimeEntries(c *gin.Context) {
}
// Get time entries from the database
timeEntries, err := models.GetTimeEntriesByUserID(c.Request.Context(), models.FromULID(userID))
timeEntries, err := models.GetTimeEntriesByUserID(c.Request.Context(), userID)
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving time entries: "+err.Error())
return
@@ -191,7 +192,7 @@ func (h *TimeEntryHandler) GetTimeEntriesByProjectID(c *gin.Context) {
}
// Get time entries from the database
timeEntries, err := models.GetTimeEntriesByProjectID(c.Request.Context(), models.FromULID(projectID))
timeEntries, err := models.GetTimeEntriesByProjectID(c.Request.Context(), types.FromULID(projectID))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving time entries: "+err.Error())
return
@@ -390,7 +391,7 @@ func (h *TimeEntryHandler) DeleteTimeEntry(c *gin.Context) {
}
// Delete time entry from the database
err = models.DeleteTimeEntry(c.Request.Context(), models.FromULID(id))
err = models.DeleteTimeEntry(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error deleting time entry: "+err.Error())
return
@@ -418,17 +419,17 @@ func convertTimeEntryToDTO(timeEntry *models.TimeEntry) dto.TimeEntryDto {
func convertCreateTimeEntryDTOToModel(dto dto.TimeEntryCreateDto) (models.TimeEntryCreate, error) {
// Convert IDs from int to ULID (this is a simplification, adjust as needed)
userID, err := models.ULIDWrapperFromString(dto.UserID)
userID, err := types.ULIDFromString(dto.UserID)
if err != nil {
return models.TimeEntryCreate{}, fmt.Errorf("invalid user ID: %w", err)
}
projectID, err := models.ULIDWrapperFromString(dto.ProjectID)
projectID, err := types.ULIDFromString(dto.ProjectID)
if err != nil {
return models.TimeEntryCreate{}, fmt.Errorf("invalid project ID: %w", err)
}
activityID, err := models.ULIDWrapperFromString(dto.ActivityID)
activityID, err := types.ULIDFromString(dto.ActivityID)
if err != nil {
return models.TimeEntryCreate{}, fmt.Errorf("invalid activity ID: %w", err)
}
@@ -450,11 +451,11 @@ func convertUpdateTimeEntryDTOToModel(dto dto.TimeEntryUpdateDto) (models.TimeEn
return models.TimeEntryUpdate{}, fmt.Errorf("invalid time entry ID: %w", err)
}
update := models.TimeEntryUpdate{
ID: models.FromULID(id),
ID: types.FromULID(id),
}
if dto.UserID != nil {
userID, err := models.ULIDWrapperFromString(*dto.UserID)
userID, err := types.ULIDFromString(*dto.UserID)
if err != nil {
return models.TimeEntryUpdate{}, fmt.Errorf("invalid user ID: %w", err)
}
@@ -462,7 +463,7 @@ func convertUpdateTimeEntryDTOToModel(dto dto.TimeEntryUpdateDto) (models.TimeEn
}
if dto.ProjectID != nil {
projectID, err := models.ULIDWrapperFromString(*dto.ProjectID)
projectID, err := types.ULIDFromString(*dto.ProjectID)
if err != nil {
return models.TimeEntryUpdate{}, fmt.Errorf("invalid project ID: %w", err)
}
@@ -470,7 +471,7 @@ func convertUpdateTimeEntryDTOToModel(dto dto.TimeEntryUpdateDto) (models.TimeEn
}
if dto.ActivityID != nil {
activityID, err := models.ULIDWrapperFromString(*dto.ActivityID)
activityID, err := types.ULIDFromString(*dto.ActivityID)
if err != nil {
return models.TimeEntryUpdate{}, fmt.Errorf("invalid activity ID: %w", err)
}
+10 -12
View File
@@ -9,6 +9,7 @@ import (
"github.com/timetracker/backend/internal/api/utils"
dto "github.com/timetracker/backend/internal/dtos"
"github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
)
// UserHandler handles user-related API endpoints
@@ -73,7 +74,7 @@ func (h *UserHandler) GetUserByID(c *gin.Context) {
}
// Get user from the database
user, err := models.GetUserByID(c.Request.Context(), models.FromULID(id))
user, err := models.GetUserByID(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving user: "+err.Error())
return
@@ -163,11 +164,8 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
// Set ID from URL
userUpdateDTO.ID = id.String()
// Set ID from URL
userUpdateDTO.ID = id.String()
// Convert DTO to Model
idWrapper := models.FromULID(id)
idWrapper := types.FromULID(id)
update := models.UserUpdate{
ID: idWrapper,
}
@@ -183,7 +181,7 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
}
if userUpdateDTO.CompanyID != nil {
if userUpdateDTO.CompanyID.Valid {
companyID, err := models.ULIDWrapperFromString(*userUpdateDTO.CompanyID.Value)
companyID, err := types.ULIDFromString(*userUpdateDTO.CompanyID.Value)
if err != nil {
utils.BadRequestResponse(c, "Invalid company ID format")
return
@@ -191,7 +189,6 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
update.CompanyID = &companyID
} else {
update.CompanyID = nil
}
}
if userUpdateDTO.HourlyRate != nil {
@@ -240,7 +237,7 @@ func (h *UserHandler) DeleteUser(c *gin.Context) {
}
// Delete user from the database
err = models.DeleteUser(c.Request.Context(), models.FromULID(id))
err = models.DeleteUser(c.Request.Context(), types.FromULID(id))
if err != nil {
utils.InternalErrorResponse(c, "Error deleting user: "+err.Error())
return
@@ -360,7 +357,7 @@ func (h *UserHandler) GetCurrentUser(c *gin.Context) {
}
// Get user from the database
user, err := models.GetUserByID(c.Request.Context(), models.FromULID(userID))
user, err := models.GetUserByID(c.Request.Context(), userID)
if err != nil {
utils.InternalErrorResponse(c, "Error retrieving user: "+err.Error())
return
@@ -397,9 +394,10 @@ func convertUserToDTO(user *models.User) dto.UserDto {
}
func convertCreateDTOToModel(dto dto.UserCreateDto) models.UserCreate {
var companyID models.ULIDWrapper
if dto.CompanyID != nil && dto.CompanyID.Valid {
companyID, _ = models.ULIDWrapperFromString(*dto.CompanyID.Value) // Ignoring error, validation happens in the model
var companyID *types.ULID
if dto.CompanyID != nil {
wrapper, _ := types.ULIDFromString(*dto.CompanyID) // Ignoring error, validation happens in the model
companyID = &wrapper
}
return models.UserCreate{