feat: Introduce Undefined function for Nullable type and refactor DTOs to use Nullable directly

This commit is contained in:
Jean Jacques Avril 2025-03-12 11:03:48 +00:00
parent 4170eb5fbd
commit b47c29cf5a
15 changed files with 126 additions and 135 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/oklog/ulid/v2" "github.com/oklog/ulid/v2"
"github.com/timetracker/backend/internal/models" "github.com/timetracker/backend/internal/models"
"github.com/timetracker/backend/internal/types"
) )
func main() { func main() {
@ -101,7 +102,7 @@ func testUserModel(ctx context.Context) {
Email: "test@example.com", Email: "test@example.com",
Password: "Test@123456", Password: "Test@123456",
Role: models.RoleUser, Role: models.RoleUser,
CompanyID: companyID, CompanyID: &companyID,
HourlyRate: 50.0, HourlyRate: 50.0,
} }
@ -196,7 +197,7 @@ func testRelationships(ctx context.Context) {
// Test invalid ID // Test invalid ID
invalidID := ulid.MustNew(ulid.Timestamp(time.Now()), ulid.DefaultEntropy()) invalidID := ulid.MustNew(ulid.Timestamp(time.Now()), ulid.DefaultEntropy())
invalidUser, err := models.GetUserByID(ctx, models.FromULID(invalidID)) invalidUser, err := models.GetUserByID(ctx, types.FromULID(invalidID))
if err != nil { if err != nil {
log.Fatalf("Error getting user with invalid ID: %v", err) log.Fatalf("Error getting user with invalid ID: %v", err)
} }

View File

@ -147,7 +147,7 @@ func (h *CompanyHandler) CreateCompany(c *gin.Context) {
func (h *CompanyHandler) UpdateCompany(c *gin.Context) { func (h *CompanyHandler) UpdateCompany(c *gin.Context) {
// Parse ID from URL // Parse ID from URL
idStr := c.Param("id") idStr := c.Param("id")
id, err := ulid.Parse(idStr) id, err := types.ULIDFromString(idStr)
if err != nil { if err != nil {
utils.BadRequestResponse(c, "Invalid company ID format") utils.BadRequestResponse(c, "Invalid company ID format")
return return
@ -160,11 +160,8 @@ func (h *CompanyHandler) UpdateCompany(c *gin.Context) {
return return
} }
// Set ID from URL
companyUpdateDTO.ID = id.String()
// Convert DTO to model // Convert DTO to model
companyUpdate := convertUpdateCompanyDTOToModel(companyUpdateDTO) companyUpdate := convertUpdateCompanyDTOToModel(companyUpdateDTO, id)
// Update company in the database // Update company in the database
company, err := models.UpdateCompany(c.Request.Context(), companyUpdate) company, err := models.UpdateCompany(c.Request.Context(), companyUpdate)
@ -234,10 +231,9 @@ func convertCreateCompanyDTOToModel(dto dto.CompanyCreateDto) models.CompanyCrea
} }
} }
func convertUpdateCompanyDTOToModel(dto dto.CompanyUpdateDto) models.CompanyUpdate { func convertUpdateCompanyDTOToModel(dto dto.CompanyUpdateDto, id types.ULID) models.CompanyUpdate {
id, _ := ulid.Parse(dto.ID)
update := models.CompanyUpdate{ update := models.CompanyUpdate{
ID: types.FromULID(id), ID: id,
} }
if dto.Name != nil { if dto.Name != nil {

View File

@ -319,16 +319,14 @@ func convertUpdateCustomerDTOToModel(dto dto.CustomerUpdateDto) (models.Customer
update.Name = dto.Name update.Name = dto.Name
} }
if dto.CompanyID != nil { if dto.CompanyID.Valid {
if dto.CompanyID.Valid { companyID, err := types.ULIDFromString(*dto.CompanyID.Value)
companyID, err := types.ULIDFromString(*dto.CompanyID.Value) if err != nil {
if err != nil { return models.CustomerUpdate{}, fmt.Errorf("invalid company ID: %w", err)
return models.CustomerUpdate{}, fmt.Errorf("invalid company ID: %w", err)
}
update.CompanyID = &companyID
} else {
update.CompanyID = nil
} }
update.CompanyID = &companyID
} else {
update.CompanyID = nil
} }
return update, nil return update, nil

View File

@ -220,7 +220,7 @@ func (h *ProjectHandler) CreateProject(c *gin.Context) {
func (h *ProjectHandler) UpdateProject(c *gin.Context) { func (h *ProjectHandler) UpdateProject(c *gin.Context) {
// Parse ID from URL // Parse ID from URL
idStr := c.Param("id") idStr := c.Param("id")
id, err := ulid.Parse(idStr) id, err := types.ULIDFromString(idStr)
if err != nil { if err != nil {
utils.BadRequestResponse(c, "Invalid project ID format") utils.BadRequestResponse(c, "Invalid project ID format")
return return
@ -233,11 +233,8 @@ func (h *ProjectHandler) UpdateProject(c *gin.Context) {
return return
} }
// Set ID from URL
projectUpdateDTO.ID = id.String()
// Convert DTO to model // Convert DTO to model
projectUpdate, err := convertUpdateProjectDTOToModel(projectUpdateDTO) projectUpdate, err := convertUpdateProjectDTOToModel(projectUpdateDTO, id)
if err != nil { if err != nil {
utils.BadRequestResponse(c, err.Error()) utils.BadRequestResponse(c, err.Error())
return return
@ -297,49 +294,51 @@ func (h *ProjectHandler) DeleteProject(c *gin.Context) {
// Helper functions for DTO conversion // Helper functions for DTO conversion
func convertProjectToDTO(project *models.Project) dto.ProjectDto { func convertProjectToDTO(project *models.Project) dto.ProjectDto {
customerId := project.CustomerID.String()
return dto.ProjectDto{ return dto.ProjectDto{
ID: project.ID.String(), ID: project.ID.String(),
CreatedAt: project.CreatedAt, CreatedAt: project.CreatedAt,
UpdatedAt: project.UpdatedAt, UpdatedAt: project.UpdatedAt,
Name: project.Name, Name: project.Name,
CustomerID: project.CustomerID.String(), CustomerID: &customerId,
} }
} }
func convertCreateProjectDTOToModel(dto dto.ProjectCreateDto) (models.ProjectCreate, error) { 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) // Convert CustomerID from int to ULID (this is a simplification, adjust as needed)
customerID, err := types.ULIDFromString(dto.CustomerID) if dto.CustomerID != nil {
if err != nil {
return models.ProjectCreate{}, fmt.Errorf("invalid customer ID: %w", err)
}
return models.ProjectCreate{ customerID, err := types.ULIDFromString(*dto.CustomerID)
Name: dto.Name, if err != nil {
CustomerID: customerID, return models.ProjectCreate{}, fmt.Errorf("invalid customer ID: %w", err)
}, nil }
create.CustomerID = &customerID
}
return create, nil
} }
func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto) (models.ProjectUpdate, error) { func convertUpdateProjectDTOToModel(dto dto.ProjectUpdateDto, id types.ULID) (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{ update := models.ProjectUpdate{
ID: types.FromULID(id), ID: id,
} }
if dto.Name != nil { if dto.Name != nil {
update.Name = dto.Name update.Name = dto.Name
} }
if dto.CustomerID != nil { if dto.CustomerID.Valid {
// Convert CustomerID from int to ULID (this is a simplification, adjust as needed) if dto.CustomerID.Value == nil {
customerID, err := types.ULIDFromString(*dto.CustomerID) update.CustomerID = nil
if err != nil {
return models.ProjectUpdate{}, fmt.Errorf("invalid customer ID: %w", err) } else {
// Convert CustomerID from int to ULID (this is a simplification, adjust as needed)
customerID, err := types.ULIDFromString(*dto.CustomerID.Value)
if err != nil {
return models.ProjectUpdate{}, fmt.Errorf("invalid customer ID: %w", err)
}
update.CustomerID = &customerID
} }
update.CustomerID = &customerID
} }
return update, nil return update, nil

View File

@ -326,7 +326,7 @@ func (h *TimeEntryHandler) CreateTimeEntry(c *gin.Context) {
func (h *TimeEntryHandler) UpdateTimeEntry(c *gin.Context) { func (h *TimeEntryHandler) UpdateTimeEntry(c *gin.Context) {
// Parse ID from URL // Parse ID from URL
idStr := c.Param("id") idStr := c.Param("id")
id, err := ulid.Parse(idStr) id, err := types.ULIDFromString(idStr)
if err != nil { if err != nil {
utils.BadRequestResponse(c, "Invalid time entry ID format") utils.BadRequestResponse(c, "Invalid time entry ID format")
return return
@ -339,11 +339,8 @@ func (h *TimeEntryHandler) UpdateTimeEntry(c *gin.Context) {
return return
} }
// Set ID from URL
timeEntryUpdateDTO.ID = id.String()
// Convert DTO to model // Convert DTO to model
timeEntryUpdate, err := convertUpdateTimeEntryDTOToModel(timeEntryUpdateDTO) timeEntryUpdate, err := convertUpdateTimeEntryDTOToModel(timeEntryUpdateDTO, id)
if err != nil { if err != nil {
utils.BadRequestResponse(c, err.Error()) utils.BadRequestResponse(c, err.Error())
return return
@ -445,13 +442,10 @@ func convertCreateTimeEntryDTOToModel(dto dto.TimeEntryCreateDto) (models.TimeEn
}, nil }, nil
} }
func convertUpdateTimeEntryDTOToModel(dto dto.TimeEntryUpdateDto) (models.TimeEntryUpdate, error) { func convertUpdateTimeEntryDTOToModel(dto dto.TimeEntryUpdateDto, id types.ULID) (models.TimeEntryUpdate, error) {
id, err := ulid.Parse(dto.ID)
if err != nil {
return models.TimeEntryUpdate{}, fmt.Errorf("invalid time entry ID: %w", err)
}
update := models.TimeEntryUpdate{ update := models.TimeEntryUpdate{
ID: types.FromULID(id), ID: id,
} }
if dto.UserID != nil { if dto.UserID != nil {

View File

@ -148,7 +148,7 @@ func (h *UserHandler) CreateUser(c *gin.Context) {
func (h *UserHandler) UpdateUser(c *gin.Context) { func (h *UserHandler) UpdateUser(c *gin.Context) {
// Parse ID from URL // Parse ID from URL
idStr := c.Param("id") idStr := c.Param("id")
id, err := ulid.Parse(idStr) id, err := types.ULIDFromString(idStr)
if err != nil { if err != nil {
utils.BadRequestResponse(c, "Invalid user ID format") utils.BadRequestResponse(c, "Invalid user ID format")
return return
@ -161,13 +161,9 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
return return
} }
// Set ID from URL
userUpdateDTO.ID = id.String()
// Convert DTO to Model // Convert DTO to Model
idWrapper := types.FromULID(id)
update := models.UserUpdate{ update := models.UserUpdate{
ID: idWrapper, ID: id,
} }
if userUpdateDTO.Email != nil { if userUpdateDTO.Email != nil {
@ -179,22 +175,23 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
if userUpdateDTO.Role != nil { if userUpdateDTO.Role != nil {
update.Role = userUpdateDTO.Role update.Role = userUpdateDTO.Role
} }
if userUpdateDTO.CompanyID != nil {
if userUpdateDTO.CompanyID.Valid { if userUpdateDTO.CompanyID.Valid {
if userUpdateDTO.CompanyID.Value != nil {
companyID, err := types.ULIDFromString(*userUpdateDTO.CompanyID.Value) companyID, err := types.ULIDFromString(*userUpdateDTO.CompanyID.Value)
if err != nil { if err != nil {
utils.BadRequestResponse(c, "Invalid company ID format") utils.BadRequestResponse(c, "Invalid company ID format")
return return
} }
update.CompanyID = &companyID update.CompanyID = types.NewNullable(companyID)
} else { } else {
update.CompanyID = nil update.CompanyID = types.Null[types.ULID]()
} }
} }
if userUpdateDTO.HourlyRate != nil { if userUpdateDTO.HourlyRate != nil {
update.HourlyRate = userUpdateDTO.HourlyRate update.HourlyRate = userUpdateDTO.HourlyRate
} }
// Update user in the database // Update user in the database
user, err := models.UpdateUser(c.Request.Context(), update) user, err := models.UpdateUser(c.Request.Context(), update)
if err != nil { if err != nil {

View File

@ -17,9 +17,7 @@ type CompanyCreateDto struct {
} }
type CompanyUpdateDto struct { type CompanyUpdateDto struct {
ID string `json:"id" example:"01HGW2BBG0000000000000000"` CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"` UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"` Name *string `json:"name" example:"Acme Corp"`
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
Name *string `json:"name" example:"Acme Corp"`
} }

View File

@ -22,11 +22,11 @@ type CustomerCreateDto struct {
} }
type CustomerUpdateDto struct { type CustomerUpdateDto struct {
ID string `json:"id" example:"01HGW2BBG0000000000000000"` ID string `json:"id" example:"01HGW2BBG0000000000000000"`
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"` CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"` UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"` LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
Name *string `json:"name" example:"John Doe"` Name *string `json:"name" example:"John Doe"`
CompanyID *types.Nullable[string] `json:"companyId" example:"01HGW2BBG0000000000000000"` CompanyID types.Nullable[string] `json:"companyId" example:"01HGW2BBG0000000000000000"`
OwnerUserID *types.Nullable[string] `json:"owningUserID" example:"01HGW2BBG0000000000000000"` OwnerUserID types.Nullable[string] `json:"owningUserID" example:"01HGW2BBG0000000000000000"`
} }

View File

@ -2,6 +2,8 @@ package dto
import ( import (
"time" "time"
"github.com/timetracker/backend/internal/types"
) )
type ProjectDto struct { type ProjectDto struct {
@ -10,19 +12,17 @@ type ProjectDto struct {
UpdatedAt time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"` UpdatedAt time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
LastEditorID string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"` LastEditorID string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
Name string `json:"name" example:"Time Tracking App"` Name string `json:"name" example:"Time Tracking App"`
CustomerID string `json:"customerId" example:"01HGW2BBG0000000000000000"` CustomerID *string `json:"customerId" example:"01HGW2BBG0000000000000000"`
} }
type ProjectCreateDto struct { type ProjectCreateDto struct {
Name string `json:"name" example:"Time Tracking App"` Name string `json:"name" example:"Time Tracking App"`
CustomerID string `json:"customerId" example:"01HGW2BBG0000000000000000"` CustomerID *string `json:"customerId" example:"01HGW2BBG0000000000000000"`
} }
type ProjectUpdateDto struct { type ProjectUpdateDto struct {
ID string `json:"id" example:"01HGW2BBG0000000000000000"` CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"` UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"` Name *string `json:"name" example:"Time Tracking App"`
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"` CustomerID types.Nullable[string] `json:"customerId" example:"01HGW2BBG0000000000000000"`
Name *string `json:"name" example:"Time Tracking App"`
CustomerID *string `json:"customerId" example:"01HGW2BBG0000000000000000"`
} }

View File

@ -29,15 +29,13 @@ type TimeEntryCreateDto struct {
} }
type TimeEntryUpdateDto struct { type TimeEntryUpdateDto struct {
ID string `json:"id" example:"01HGW2BBG0000000000000000"` CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"` UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"` UserID *string `json:"userId" example:"01HGW2BBG0000000000000000"`
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"` ProjectID *string `json:"projectId" example:"01HGW2BBG0000000000000000"`
UserID *string `json:"userId" example:"01HGW2BBG0000000000000000"` ActivityID *string `json:"activityId" example:"01HGW2BBG0000000000000000"`
ProjectID *string `json:"projectId" example:"01HGW2BBG0000000000000000"` Start *time.Time `json:"start" example:"2024-01-01T08:00:00Z"`
ActivityID *string `json:"activityId" example:"01HGW2BBG0000000000000000"` End *time.Time `json:"end" example:"2024-01-01T17:00:00Z"`
Start *time.Time `json:"start" example:"2024-01-01T08:00:00Z"` Description *string `json:"description" example:"Working on the Time Tracking App"`
End *time.Time `json:"end" example:"2024-01-01T17:00:00Z"` Billable *int `json:"billable" example:"100"` // Percentage (0-100)
Description *string `json:"description" example:"Working on the Time Tracking App"`
Billable *int `json:"billable" example:"100"` // Percentage (0-100)
} }

View File

@ -26,13 +26,12 @@ type UserCreateDto struct {
} }
type UserUpdateDto struct { type UserUpdateDto struct {
ID string `json:"id" example:"01HGW2BBG0000000000000000"` CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"` UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"` LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"` Email *string `json:"email" example:"test@example.com"`
Email *string `json:"email" example:"test@example.com"` Password *string `json:"password" example:"password123"`
Password *string `json:"password" example:"password123"` Role *string `json:"role" example:"admin"`
Role *string `json:"role" example:"admin"` CompanyID types.Nullable[string] `json:"companyId" example:"01HGW2BBG0000000000000000"`
CompanyID *types.Nullable[string] `json:"companyId" example:"01HGW2BBG0000000000000000"` HourlyRate *float64 `json:"hourlyRate" example:"50.00"`
HourlyRate *float64 `json:"hourlyRate" example:"50.00"`
} }

View File

@ -1,7 +1,6 @@
package models package models
import ( import (
"fmt"
"math/rand" "math/rand"
"time" "time"
@ -24,7 +23,6 @@ func (eb *EntityBase) BeforeCreate(tx *gorm.DB) error {
entropy := ulid.Monotonic(rand.New(rand.NewSource(time.Now().UnixNano())), 0) entropy := ulid.Monotonic(rand.New(rand.NewSource(time.Now().UnixNano())), 0)
newID := ulid.MustNew(ulid.Timestamp(time.Now()), entropy) newID := ulid.MustNew(ulid.Timestamp(time.Now()), entropy)
eb.ID = types.ULID{ULID: newID} eb.ID = types.ULID{ULID: newID}
fmt.Println("Generated ID:", eb.ID)
} }
return nil return nil
} }

View File

@ -13,8 +13,8 @@ import (
// Project represents a project in the system // Project represents a project in the system
type Project struct { type Project struct {
EntityBase EntityBase
Name string `gorm:"column:name;not null"` Name string `gorm:"column:name;not null"`
CustomerID types.ULID `gorm:"column:customer_id;type:bytea;not null"` CustomerID *types.ULID `gorm:"column:customer_id;type:bytea;not null"`
// Relationships (for Eager Loading) // Relationships (for Eager Loading)
Customer *Customer `gorm:"foreignKey:CustomerID"` Customer *Customer `gorm:"foreignKey:CustomerID"`
@ -28,7 +28,7 @@ func (Project) TableName() string {
// ProjectCreate contains the fields for creating a new project // ProjectCreate contains the fields for creating a new project
type ProjectCreate struct { type ProjectCreate struct {
Name string Name string
CustomerID types.ULID CustomerID *types.ULID
} }
// ProjectUpdate contains the updatable fields of a project // ProjectUpdate contains the updatable fields of a project
@ -122,12 +122,14 @@ func CreateProject(ctx context.Context, create ProjectCreate) (*Project, error)
} }
// Check if the customer exists // Check if the customer exists
customer, err := GetCustomerByID(ctx, create.CustomerID) if create.CustomerID == nil {
if err != nil { customer, err := GetCustomerByID(ctx, *create.CustomerID)
return nil, fmt.Errorf("error checking the customer: %w", err) if err != nil {
} return nil, fmt.Errorf("error checking the customer: %w", err)
if customer == nil { }
return nil, errors.New("the specified customer does not exist") if customer == nil {
return nil, errors.New("the specified customer does not exist")
}
} }
project := Project{ project := Project{

View File

@ -63,12 +63,12 @@ type UserCreate struct {
// UserUpdate contains the updatable fields of a user // UserUpdate contains the updatable fields of a user
type UserUpdate struct { type UserUpdate struct {
ID types.ULID `gorm:"-"` // Exclude from updates ID types.ULID `gorm:"-"` // Exclude from updates
Email *string `gorm:"column:email"` Email *string `gorm:"column:email"`
Password *string `gorm:"-"` // Not stored directly in DB Password *string `gorm:"-"` // Not stored directly in DB
Role *string `gorm:"column:role"` Role *string `gorm:"column:role"`
CompanyID *types.ULID `gorm:"column:company_id"` CompanyID types.Nullable[types.ULID] `gorm:"column:company_id"`
HourlyRate *float64 `gorm:"column:hourly_rate"` HourlyRate *float64 `gorm:"column:hourly_rate"`
} }
// PasswordData contains the data for password hash and salt // PasswordData contains the data for password hash and salt
@ -448,13 +448,15 @@ func UpdateUser(ctx context.Context, update UserUpdate) (*User, error) {
} }
// If CompanyID is updated, check if it exists // If CompanyID is updated, check if it exists
if update.CompanyID != nil && (user.CompanyID == nil || update.CompanyID.Compare(*user.CompanyID) != 0) { if update.CompanyID.Valid && update.CompanyID.Value != nil {
var companyCount int64 if user.CompanyID == nil || *update.CompanyID.Value != *user.CompanyID {
if err := tx.Model(&Company{}).Where("id = ?", *update.CompanyID).Count(&companyCount).Error; err != nil { var companyCount int64
return fmt.Errorf("error checking company: %w", err) if err := tx.Model(&Company{}).Where("id = ?", *update.CompanyID.Value).Count(&companyCount).Error; err != nil {
} return fmt.Errorf("error checking company: %w", err)
if companyCount == 0 { }
return errors.New("the specified company does not exist") if companyCount == 0 {
return errors.New("the specified company does not exist")
}
} }
} }
@ -484,8 +486,13 @@ func UpdateUser(ctx context.Context, update UserUpdate) (*User, error) {
if update.Role != nil { if update.Role != nil {
updates["role"] = *update.Role updates["role"] = *update.Role
} }
if update.CompanyID != nil { if update.CompanyID.Valid {
updates["company_id"] = *update.CompanyID if update.CompanyID.Value == nil {
updates["company_id"] = nil
} else {
updates["company_id"] = *update.CompanyID.Value
}
} }
if update.HourlyRate != nil { if update.HourlyRate != nil {
updates["hourly_rate"] = *update.HourlyRate updates["hourly_rate"] = *update.HourlyRate

View File

@ -18,6 +18,10 @@ func NewNullable[T any](value T) Nullable[T] {
// Null erstellt eine leere Nullable-Instanz (ungesetzt) // Null erstellt eine leere Nullable-Instanz (ungesetzt)
func Null[T any]() Nullable[T] { func Null[T any]() Nullable[T] {
return Nullable[T]{Valid: true}
}
func Undefined[T any]() Nullable[T] {
return Nullable[T]{Valid: false} return Nullable[T]{Valid: false}
} }