feat: Introduce Nullable type for optional fields and update user DTOs accordingly
This commit is contained in:
@@ -182,12 +182,17 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
|
||||
update.Role = userUpdateDTO.Role
|
||||
}
|
||||
if userUpdateDTO.CompanyID != nil {
|
||||
companyID, err := models.ULIDWrapperFromString(*userUpdateDTO.CompanyID)
|
||||
if err != nil {
|
||||
utils.BadRequestResponse(c, "Invalid company ID format")
|
||||
return
|
||||
if userUpdateDTO.CompanyID.Valid {
|
||||
companyID, err := models.ULIDWrapperFromString(*userUpdateDTO.CompanyID.Value)
|
||||
if err != nil {
|
||||
utils.BadRequestResponse(c, "Invalid company ID format")
|
||||
return
|
||||
}
|
||||
update.CompanyID = &companyID
|
||||
} else {
|
||||
update.CompanyID = nil
|
||||
|
||||
}
|
||||
update.CompanyID = &companyID
|
||||
}
|
||||
if userUpdateDTO.HourlyRate != nil {
|
||||
update.HourlyRate = userUpdateDTO.HourlyRate
|
||||
@@ -393,8 +398,8 @@ func convertUserToDTO(user *models.User) dto.UserDto {
|
||||
|
||||
func convertCreateDTOToModel(dto dto.UserCreateDto) models.UserCreate {
|
||||
var companyID models.ULIDWrapper
|
||||
if dto.CompanyID != nil {
|
||||
companyID, _ = models.ULIDWrapperFromString(*dto.CompanyID) // Ignoring error, validation happens in the model
|
||||
if dto.CompanyID != nil && dto.CompanyID.Valid {
|
||||
companyID, _ = models.ULIDWrapperFromString(*dto.CompanyID.Value) // Ignoring error, validation happens in the model
|
||||
}
|
||||
|
||||
return models.UserCreate{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dto
|
||||
package helper
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
@@ -2,6 +2,8 @@ package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/timetracker/backend/internal/types"
|
||||
)
|
||||
|
||||
type UserDto struct {
|
||||
@@ -16,21 +18,21 @@ type UserDto struct {
|
||||
}
|
||||
|
||||
type UserCreateDto struct {
|
||||
Email string `json:"email" example:"test@example.com"`
|
||||
Password string `json:"password" example:"password123"`
|
||||
Role string `json:"role" example:"admin"`
|
||||
CompanyID *string `json:"companyId" example:"01HGW2BBG0000000000000000"`
|
||||
HourlyRate float64 `json:"hourlyRate" example:"50.00"`
|
||||
Email string `json:"email" example:"test@example.com"`
|
||||
Password string `json:"password" example:"password123"`
|
||||
Role string `json:"role" example:"admin"`
|
||||
CompanyID *types.Nullable[string] `json:"companyId" example:"01HGW2BBG0000000000000000"`
|
||||
HourlyRate float64 `json:"hourlyRate" example:"50.00"`
|
||||
}
|
||||
|
||||
type UserUpdateDto struct {
|
||||
ID string `json:"id" example:"01HGW2BBG0000000000000000"`
|
||||
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
|
||||
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
|
||||
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
|
||||
Email *string `json:"email" example:"test@example.com"`
|
||||
Password *string `json:"password" example:"password123"`
|
||||
Role *string `json:"role" example:"admin"`
|
||||
CompanyID *string `json:"companyId" example:"01HGW2BBG0000000000000000"`
|
||||
HourlyRate *float64 `json:"hourlyRate" example:"50.00"`
|
||||
ID string `json:"id" example:"01HGW2BBG0000000000000000"`
|
||||
CreatedAt *time.Time `json:"createdAt" example:"2024-01-01T00:00:00Z"`
|
||||
UpdatedAt *time.Time `json:"updatedAt" example:"2024-01-01T00:00:00Z"`
|
||||
LastEditorID *string `json:"lastEditorID" example:"01HGW2BBG0000000000000000"`
|
||||
Email *string `json:"email" example:"test@example.com"`
|
||||
Password *string `json:"password" example:"password123"`
|
||||
Role *string `json:"role" example:"admin"`
|
||||
CompanyID *types.Nullable[string] `json:"companyId" example:"01HGW2BBG0000000000000000"`
|
||||
HourlyRate *float64 `json:"hourlyRate" example:"50.00"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Nullable[T] - Generischer Typ für optionale Werte (nullable fields)
|
||||
type Nullable[T any] struct {
|
||||
Value *T // Der tatsächliche Wert (kann nil sein)
|
||||
Valid bool // Gibt an, ob der Wert gesetzt wurde
|
||||
}
|
||||
|
||||
// NewNullable erstellt eine gültige Nullable-Instanz
|
||||
func NewNullable[T any](value T) Nullable[T] {
|
||||
return Nullable[T]{Value: &value, Valid: true}
|
||||
}
|
||||
|
||||
// Null erstellt eine leere Nullable-Instanz (ungesetzt)
|
||||
func Null[T any]() Nullable[T] {
|
||||
return Nullable[T]{Valid: false}
|
||||
}
|
||||
|
||||
// MarshalJSON - Serialisiert `Nullable[T]` korrekt ins JSON-Format
|
||||
func (n Nullable[T]) MarshalJSON() ([]byte, error) {
|
||||
if !n.Valid {
|
||||
return []byte("null"), nil // Wenn nicht valid, dann NULL
|
||||
}
|
||||
return json.Marshal(n.Value) // Serialisiert den tatsächlichen Wert
|
||||
}
|
||||
|
||||
// UnmarshalJSON - Deserialisiert JSON in `Nullable[T]`
|
||||
func (n *Nullable[T]) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == "null" {
|
||||
n.Valid = true // Wert wurde gesetzt, aber auf NULL
|
||||
n.Value = nil // Explizit NULL setzen
|
||||
return nil
|
||||
}
|
||||
|
||||
var v T
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return fmt.Errorf("invalid JSON for Nullable: %w", err)
|
||||
}
|
||||
|
||||
n.Value = &v
|
||||
n.Valid = true
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user