From e4038aabb532e13f2a77670de2c34210937c9d3b Mon Sep 17 00:00:00 2001 From: bRight36691 <141995847+bRight36691@users.noreply.github.com> Date: Thu, 13 Feb 2025 11:09:51 +0700 Subject: [PATCH 1/6] add validation lifestyle field --- go.mod | 3 + go.sum | 8 +++ internal/core/domain/user.go | 55 +++++++++++++++++++ internal/handlers/dto/user_body.go | 2 +- .../user_handler_update_user_information.go | 4 +- pkg/utils/validation.go | 21 +++++++ 6 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 pkg/utils/validation.go diff --git a/go.mod b/go.mod index db078a6..28bbb07 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/caarlos0/env/v11 v11.3.1 github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df github.com/go-playground/validator v9.31.0+incompatible + github.com/go-playground/validator/v10 v10.24.0 github.com/gofiber/fiber/v2 v2.52.6 github.com/gofiber/swagger v1.1.1 github.com/golang-jwt/jwt/v4 v4.5.1 @@ -41,6 +42,7 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect @@ -65,6 +67,7 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.58.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.35.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect diff --git a/go.sum b/go.sum index eca3cde..7bc7fc4 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vaui github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E= github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= @@ -53,12 +55,16 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA= github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofiber/swagger v1.1.1 h1:FZVhVQQ9s1ZKLHL/O0loLh49bYB5l1HEAgxDlcTtkRA= @@ -127,6 +133,8 @@ golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/core/domain/user.go b/internal/core/domain/user.go index 0ae3167..8b544f4 100644 --- a/internal/core/domain/user.go +++ b/internal/core/domain/user.go @@ -64,6 +64,61 @@ const ( SelfEmployed Lifestyle = "Self-Employed" ) +// Set of valid lifestyle values (for quick lookup) +var validLifestyles = map[Lifestyle]struct{}{ + Active: {}, + Creative: {}, + Social: {}, + Relaxed: {}, + + Football: {}, + Basketball: {}, + Tennis: {}, + Swimming: {}, + Running: {}, + Cycling: {}, + Badminton: {}, + Yoga: {}, + GymAndFitness: {}, + + Music: {}, + Dancing: {}, + Photography: {}, + Painting: {}, + Gaming: {}, + Reading: {}, + Writing: {}, + DIYAndCrafting: {}, + Cooking: {}, + + Extrovert: {}, + Introvert: {}, + NightOwl: {}, + EarlyBird: {}, + + Traveler: {}, + Backpacker: {}, + NatureLover: {}, + Camping: {}, + BeachLover: {}, + + DogLover: {}, + CatLover: {}, + + Freelancer: {}, + Entrepreneur: {}, + OfficeWorker: {}, + RemoteWorker: {}, + Student: {}, + SelfEmployed: {}, +} + +// IsValid checks if the lifestyle value is in the valid set. +func (l Lifestyle) IsValid() bool { + _, exists := validLifestyles[l] + return exists +} + type User struct { ID uuid.UUID `json:"id" gorm:"type:uuid;default:uuid_generate_v4();primaryKey"` CreateAt time.Time `json:"createAt" gorm:"autoCreateTime"` diff --git a/internal/handlers/dto/user_body.go b/internal/handlers/dto/user_body.go index 1a90e29..fa0c21f 100644 --- a/internal/handlers/dto/user_body.go +++ b/internal/handlers/dto/user_body.go @@ -28,5 +28,5 @@ type UserInformationRequestBody struct { Gender string `json:"gender"` BirthDate time.Time `json:"birthDate"` StudentEvidence string `json:"studentEvidence"` - Lifestyles []domain.Lifestyle `json:"lifestyles" gorm:"default:null"` + Lifestyles []domain.Lifestyle `json:"lifestyles" validate:"lifestyle" gorm:"default:null"` } diff --git a/internal/handlers/user_handler_update_user_information.go b/internal/handlers/user_handler_update_user_information.go index 3586da5..d9fe148 100644 --- a/internal/handlers/user_handler_update_user_information.go +++ b/internal/handlers/user_handler_update_user_information.go @@ -7,7 +7,8 @@ import ( "github.com/PitiNarak/condormhub-backend/internal/handlers/dto" "github.com/PitiNarak/condormhub-backend/pkg/error_handler" "github.com/PitiNarak/condormhub-backend/pkg/http_response" - "github.com/go-playground/validator" + "github.com/PitiNarak/condormhub-backend/pkg/utils" + "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" ) @@ -38,6 +39,7 @@ func (h *UserHandler) UpdateUserInformation(c *fiber.Ctx) error { } validate := validator.New() + validate.RegisterValidation("lifestyle", utils.ValidateLifestyles) if err := validate.Struct(requestBody); err != nil { return error_handler.BadRequestError(err, "your request body is incorrect") diff --git a/pkg/utils/validation.go b/pkg/utils/validation.go new file mode 100644 index 0000000..3f61f10 --- /dev/null +++ b/pkg/utils/validation.go @@ -0,0 +1,21 @@ +package utils + +import ( + "github.com/PitiNarak/condormhub-backend/internal/core/domain" + "github.com/go-playground/validator/v10" +) + +// Custom validator for Lifestyles +func ValidateLifestyles(fl validator.FieldLevel) bool { + lifestyles, ok := fl.Field().Interface().([]domain.Lifestyle) + if !ok { + return false + } + + for _, l := range lifestyles { + if !l.IsValid() { + return false + } + } + return true +} From bfccaae4214d5d2dad6e3e0dd8fa1b4b5cb304da Mon Sep 17 00:00:00 2001 From: bRight36691 <141995847+bRight36691@users.noreply.github.com> Date: Sat, 22 Feb 2025 23:48:23 +0700 Subject: [PATCH 2/6] Change to store lifestyle-tag as a array in DB --- cmd/migrate/main.go | 60 ++++++++++++- go.mod | 1 + go.sum | 2 + internal/core/domain/user.go | 107 +++++++++++++++++++---- internal/core/ports/user_port.go | 2 +- internal/core/services/user_service.go | 28 +----- internal/handlers/dto/user_body.go | 18 ++-- internal/repositories/user_repository.go | 3 +- pkg/utils/validation.go | 2 +- 9 files changed, 165 insertions(+), 58 deletions(-) diff --git a/cmd/migrate/main.go b/cmd/migrate/main.go index 3a7e276..4fd72f6 100644 --- a/cmd/migrate/main.go +++ b/cmd/migrate/main.go @@ -6,10 +6,66 @@ import ( "github.com/PitiNarak/condormhub-backend/internal/config" "github.com/PitiNarak/condormhub-backend/internal/core/domain" "github.com/PitiNarak/condormhub-backend/internal/databases" - "github.com/gofiber/fiber/v2/log" + "gorm.io/gorm" ) +func CreateEnum(db *gorm.DB) { + query := ` + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = "lifestyle_tag") THEN + CREATE TYPE lifestyle_tag AS ENUM ( + "Active", + "Creative", + "Social", + "Relaxed", + + "Football", + "Basketball", + "Tennis", + "Swimming", + "Running", + "Cycling", + "Badminton", + "Yoga", + "Gym & Fitness", + + "Music", + "Dancing", + "Photography", + "Painting", + "Gaming", + "Reading", + "Writing", + "DIY & Crafting", + "Cooking", + + "Extrovert", + "Introvert", + "Night Owl", + "Early Bird", + + "Traveler", + "Backpacker", + "Nature Lover", + "Camping", + "Beach Lover", + + "Dog Lover", + "Cat Lover", + + "Freelancer", + "Entrepreneur", + "Office Worker", + "Remote Worker", + "Student", + "Self-Employed"); + END IF; + END $$; + ` + db.Exec(query) +} + func main() { config := config.Load() @@ -18,6 +74,8 @@ func main() { log.Fatalf("Database connection failed: %v", err) } + CreateEnum(db) + if err := db.AutoMigrate( &domain.SampleLog{}, &domain.User{}, diff --git a/go.mod b/go.mod index 28bbb07..3dbeda1 100644 --- a/go.mod +++ b/go.mod @@ -58,6 +58,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index 7bc7fc4..e0da161 100644 --- a/go.sum +++ b/go.sum @@ -97,6 +97,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= diff --git a/internal/core/domain/user.go b/internal/core/domain/user.go index 8b544f4..30345b5 100644 --- a/internal/core/domain/user.go +++ b/internal/core/domain/user.go @@ -1,6 +1,9 @@ package domain import ( + "database/sql/driver" + "fmt" + "strings" "time" "github.com/google/uuid" @@ -119,26 +122,94 @@ func (l Lifestyle) IsValid() bool { return exists } +type LifestyleArray []Lifestyle + +// Scan converts PostgreSQL data to LifestyleArray +func (l *LifestyleArray) Scan(value interface{}) error { + // Handle the empty array case from PostgreSQL ("{}") + if value == "{}" { + *l = []Lifestyle{} + return nil + } + + // Handle the case where the value is a string (PostgreSQL array format) + str, ok := value.(string) + if ok { + // Remove the surrounding curly braces and split by comma + str = str[1 : len(str)-1] // Removing the {} characters + // Split by commas to extract each element + elements := strings.Split(str, ",") + // Trim spaces from each element and convert to Lifestyle enum + var lifestyles []Lifestyle + for _, elem := range elements { + elem = strings.TrimSpace(elem) // Removing any extra spaces + lifestyles = append(lifestyles, Lifestyle(elem)) + } + *l = lifestyles + return nil + } + + // Handle the case where the value is a byte slice (PostgreSQL array format) + byteArray, ok := value.([]byte) + if ok { + // Convert byte array to string and process similarly + str := string(byteArray) + str = str[1 : len(str)-1] // Removing the {} characters + elements := strings.Split(str, ",") + var lifestyles []Lifestyle + for _, elem := range elements { + elem = strings.TrimSpace(elem) + lifestyles = append(lifestyles, Lifestyle(elem)) + } + *l = lifestyles + return nil + } + + // If the value is neither a string nor a byte slice, return an error + return fmt.Errorf("failed to scan LifestyleArray: %v", value) +} + +// Value converts LifestyleArray to database format +func (l LifestyleArray) Value() (driver.Value, error) { + if len(l) == 0 { + return "{}", nil // Empty PostgreSQL array + } + + // jsonData, err := json.Marshal(l) + // fmt.Println(jsonData) + + strLifestyles := make([]string, len(l)) + for i, lifestyle := range l { + strLifestyles[i] = string(lifestyle) + } + + // Return the PostgreSQL array format without any extra string ("Lifestyles: ") + return "{" + strings.Join(strLifestyles, ",") + "}", nil + + // if err != nil { + // return nil, err + // } + // return string(jsonData), nil +} + type User struct { - ID uuid.UUID `json:"id" gorm:"type:uuid;default:uuid_generate_v4();primaryKey"` - CreateAt time.Time `json:"createAt" gorm:"autoCreateTime"` - UpdateAt time.Time `json:"updateAt" gorm:"autoUpdateTime"` - Username string `json:"username" gorm:"unique" validate:"required"` - Password string `json:"-" validate:"required,min=8"` - Email string `json:"email" gorm:"unique" validate:"required,email"` - Firstname string `json:"firstname"` - Lastname string `json:"lastname"` - NationalID string `json:"nationalID" ` - Gender string `json:"gender"` - BirthDate time.Time `json:"birthDate" gorm:"type:DATE;default:null"` - IsVerified bool `json:"isVerified" gorm:"default:false"` - Role *Role `json:"role" gorm:"default:null"` - FilledPersonalInfo bool `json:"filledPersonalInfo" gorm:"default:false"` - Lifestyle1 *Lifestyle `json:"lifestyle1" gorm:"default:null"` - Lifestyle2 *Lifestyle `json:"lifestyle2" gorm:"default:null"` - Lifestyle3 *Lifestyle `json:"lifestyle3" gorm:"default:null"` + ID uuid.UUID `json:"id" gorm:"type:uuid;default:uuid_generate_v4();primaryKey"` + CreateAt time.Time `json:"createAt" gorm:"autoCreateTime"` + UpdateAt time.Time `json:"updateAt" gorm:"autoUpdateTime"` + Username string `json:"username" gorm:"unique" validate:"required"` + Password string `json:"-" validate:"required,min=8"` + Email string `json:"email" gorm:"unique" validate:"required,email"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + NationalID string `json:"nationalID" ` + Gender string `json:"gender"` + BirthDate time.Time `json:"birthDate" gorm:"type:DATE;default:null"` + IsVerified bool `json:"isVerified" gorm:"default:false"` + Role *Role `json:"role" gorm:"default:null"` + FilledPersonalInfo bool `json:"filledPersonalInfo" gorm:"default:false"` + Lifestyles LifestyleArray `json:"lifestyles" validate:"lifestyle" gorm:"type:lifestyle_tag[]"` // studentEvidence StudentEvidence string `json:"studentEvidence"` - IsStudentVerified bool `json:"isStudentVerified" gorm:"default:false"` + IsStudentVerified bool `json:"isStudentVerified" gorm:"default:false" ` } diff --git a/internal/core/ports/user_port.go b/internal/core/ports/user_port.go index 80db901..405fcdf 100644 --- a/internal/core/ports/user_port.go +++ b/internal/core/ports/user_port.go @@ -10,7 +10,7 @@ import ( type UserRepository interface { Create(user *domain.User) error GetUserByID(userID uuid.UUID) (*domain.User, error) - UpdateInformation(userID uuid.UUID, data domain.User) error + UpdateInformation(userID uuid.UUID, data dto.UserInformationRequestBody) error UpdateUser(user *domain.User) error GetUserByEmail(email string) (*domain.User, error) DeleteAccount(userID uuid.UUID) error diff --git a/internal/core/services/user_service.go b/internal/core/services/user_service.go index c2e82e5..49777b4 100644 --- a/internal/core/services/user_service.go +++ b/internal/core/services/user_service.go @@ -104,33 +104,7 @@ func (s *UserService) UpdateInformation(userID uuid.UUID, data dto.UserInformati data.Password = string(hashedPassword) } - var updateData domain.User - - updateData.Username = data.Username - updateData.Password = data.Password - updateData.Firstname = data.Firstname - updateData.Lastname = data.Lastname - updateData.NationalID = data.NationalID - updateData.Gender = data.Gender - updateData.BirthDate = data.BirthDate - updateData.StudentEvidence = data.StudentEvidence - - // Reset Lifestyle fields before assigning new values - updateData.Lifestyle1 = nil - updateData.Lifestyle2 = nil - updateData.Lifestyle3 = nil - - if len(data.Lifestyles) > 0 { - updateData.Lifestyle1 = &data.Lifestyles[0] - } - if len(data.Lifestyles) > 1 { - updateData.Lifestyle2 = &data.Lifestyles[1] - } - if len(data.Lifestyles) > 2 { - updateData.Lifestyle3 = &data.Lifestyles[2] - } - - err := s.userRepo.UpdateInformation(userID, updateData) + err := s.userRepo.UpdateInformation(userID, data) if err != nil { return nil, err } diff --git a/internal/handlers/dto/user_body.go b/internal/handlers/dto/user_body.go index fa0c21f..6515a24 100644 --- a/internal/handlers/dto/user_body.go +++ b/internal/handlers/dto/user_body.go @@ -20,13 +20,13 @@ type VerifyRequestBody struct { } type UserInformationRequestBody struct { - Username string `json:"username" gorm:"unique"` - Password string `json:"password" validate:"omitempty,min=8"` - Firstname string `json:"firstname"` - Lastname string `json:"lastname"` - NationalID string `json:"nationalID"` - Gender string `json:"gender"` - BirthDate time.Time `json:"birthDate"` - StudentEvidence string `json:"studentEvidence"` - Lifestyles []domain.Lifestyle `json:"lifestyles" validate:"lifestyle" gorm:"default:null"` + Username string `json:"username" gorm:"unique"` + Password string `json:"password" validate:"omitempty,min=8"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + NationalID string `json:"nationalID"` + Gender string `json:"gender"` + BirthDate time.Time `json:"birthDate"` + StudentEvidence string `json:"studentEvidence"` + Lifestyles domain.LifestyleArray `json:"lifestyles" validate:"lifestyle" gorm:"type:lifestyle_tag[]"` } diff --git a/internal/repositories/user_repository.go b/internal/repositories/user_repository.go index bf4f4e2..2d67d25 100644 --- a/internal/repositories/user_repository.go +++ b/internal/repositories/user_repository.go @@ -3,6 +3,7 @@ package repositories import ( "github.com/PitiNarak/condormhub-backend/internal/core/domain" "github.com/PitiNarak/condormhub-backend/internal/core/ports" + "github.com/PitiNarak/condormhub-backend/internal/handlers/dto" "github.com/PitiNarak/condormhub-backend/pkg/error_handler" "github.com/google/uuid" @@ -61,7 +62,7 @@ func (r *UserRepo) UpdateUser(user *domain.User) error { return nil } -func (r *UserRepo) UpdateInformation(userID uuid.UUID, data domain.User) error { +func (r *UserRepo) UpdateInformation(userID uuid.UUID, data dto.UserInformationRequestBody) error { err := r.db.Model(&domain.User{}).Where("id = ?", userID).Updates(data).Error if err != nil { return error_handler.InternalServerError(err, "failed to update user information") diff --git a/pkg/utils/validation.go b/pkg/utils/validation.go index 3f61f10..f7b172d 100644 --- a/pkg/utils/validation.go +++ b/pkg/utils/validation.go @@ -7,7 +7,7 @@ import ( // Custom validator for Lifestyles func ValidateLifestyles(fl validator.FieldLevel) bool { - lifestyles, ok := fl.Field().Interface().([]domain.Lifestyle) + lifestyles, ok := fl.Field().Interface().(domain.LifestyleArray) if !ok { return false } From 381c5118022fdd6b38ebc4e89e17db40b1b1de91 Mon Sep 17 00:00:00 2001 From: bRight36691 <141995847+bRight36691@users.noreply.github.com> Date: Sun, 23 Feb 2025 23:39:18 +0700 Subject: [PATCH 3/6] Update swagger --- docs/docs.go | 13 +++++-------- docs/swagger.json | 13 +++++-------- docs/swagger.yaml | 10 ++++------ go.mod | 6 ++---- go.sum | 12 ++++-------- 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 1cbce17..1c79085 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -873,14 +873,11 @@ const docTemplate = `{ "lastname": { "type": "string" }, - "lifestyle1": { - "$ref": "#/definitions/domain.Lifestyle" - }, - "lifestyle2": { - "$ref": "#/definitions/domain.Lifestyle" - }, - "lifestyle3": { - "$ref": "#/definitions/domain.Lifestyle" + "lifestyles": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Lifestyle" + } }, "nationalID": { "type": "string" diff --git a/docs/swagger.json b/docs/swagger.json index 1eaa8be..a7d9c68 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -865,14 +865,11 @@ "lastname": { "type": "string" }, - "lifestyle1": { - "$ref": "#/definitions/domain.Lifestyle" - }, - "lifestyle2": { - "$ref": "#/definitions/domain.Lifestyle" - }, - "lifestyle3": { - "$ref": "#/definitions/domain.Lifestyle" + "lifestyles": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Lifestyle" + } }, "nationalID": { "type": "string" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d6433a1..6efc896 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -113,12 +113,10 @@ definitions: type: boolean lastname: type: string - lifestyle1: - $ref: '#/definitions/domain.Lifestyle' - lifestyle2: - $ref: '#/definitions/domain.Lifestyle' - lifestyle3: - $ref: '#/definitions/domain.Lifestyle' + lifestyles: + items: + $ref: '#/definitions/domain.Lifestyle' + type: array nationalID: type: string role: diff --git a/go.mod b/go.mod index 3dbeda1..0d10b7d 100644 --- a/go.mod +++ b/go.mod @@ -56,9 +56,8 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/lib/pq v1.10.9 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -66,8 +65,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/swaggo/files/v2 v2.0.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.58.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect + github.com/valyala/fasthttp v1.59.0 // indirect golang.org/x/net v0.35.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect diff --git a/go.sum b/go.sum index e0da161..827dd41 100644 --- a/go.sum +++ b/go.sum @@ -89,16 +89,14 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -125,10 +123,8 @@ github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE= -github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI= +github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= From ae838c476f0de98523b4f4ede7b27429acc2dd44 Mon Sep 17 00:00:00 2001 From: bRight36691 <141995847+bRight36691@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:14:29 +0700 Subject: [PATCH 4/6] fix response lifestyle-tag --- internal/core/domain/user.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/internal/core/domain/user.go b/internal/core/domain/user.go index 30345b5..a019be6 100644 --- a/internal/core/domain/user.go +++ b/internal/core/domain/user.go @@ -3,6 +3,7 @@ package domain import ( "database/sql/driver" "fmt" + "regexp" "strings" "time" @@ -135,16 +136,22 @@ func (l *LifestyleArray) Scan(value interface{}) error { // Handle the case where the value is a string (PostgreSQL array format) str, ok := value.(string) if ok { - // Remove the surrounding curly braces and split by comma - str = str[1 : len(str)-1] // Removing the {} characters - // Split by commas to extract each element - elements := strings.Split(str, ",") - // Trim spaces from each element and convert to Lifestyle enum + // Remove surrounding curly braces + str = str[1 : len(str)-1] // Removes `{}` + + // Use regex to correctly split elements handling quotes + re := regexp.MustCompile(`"([^"]*)"|([^,]+)`) + matches := re.FindAllStringSubmatch(str, -1) + var lifestyles []Lifestyle - for _, elem := range elements { - elem = strings.TrimSpace(elem) // Removing any extra spaces - lifestyles = append(lifestyles, Lifestyle(elem)) + for _, match := range matches { + if match[1] != "" { + lifestyles = append(lifestyles, Lifestyle(match[1])) // Remove surrounding quotes + } else { + lifestyles = append(lifestyles, Lifestyle(match[2])) + } } + *l = lifestyles return nil } From 6c0833c5e9e284053c1d04513128191e035cd2ad Mon Sep 17 00:00:00 2001 From: Thanapon Johdee Date: Mon, 24 Feb 2025 20:49:46 +0700 Subject: [PATCH 5/6] fix(deps): add missing track file --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 55de617..dda5a5f 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 + github.com/redis/go-redis/v9 v9.7.1 github.com/swaggo/swag v1.16.4 golang.org/x/crypto v0.33.0 gorm.io/driver/postgres v1.5.11 @@ -42,9 +43,9 @@ require ( require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect @@ -64,7 +65,6 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/redis/go-redis/v9 v9.7.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/swaggo/files/v2 v2.0.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect From 4dc85bddbc4f0307f02059de6406ac03bd2f7fe4 Mon Sep 17 00:00:00 2001 From: bRight36691 <141995847+bRight36691@users.noreply.github.com> Date: Tue, 25 Feb 2025 16:11:07 +0700 Subject: [PATCH 6/6] recheck and run lint --- cmd/migrate/main.go | 81 ++++++++++--------- .../user_handler_update_user_information.go | 5 +- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/cmd/migrate/main.go b/cmd/migrate/main.go index ce14519..ea7afe6 100644 --- a/cmd/migrate/main.go +++ b/cmd/migrate/main.go @@ -13,53 +13,54 @@ import ( func CreateEnum(db *gorm.DB) { query := ` DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = "lifestyle_tag") THEN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'lifestyle_tag') THEN CREATE TYPE lifestyle_tag AS ENUM ( - "Active", - "Creative", - "Social", - "Relaxed", + 'Active', + 'Creative', + 'Social', + 'Relaxed', - "Football", - "Basketball", - "Tennis", - "Swimming", - "Running", - "Cycling", - "Badminton", - "Yoga", - "Gym & Fitness", + 'Football', + 'Basketball', + 'Tennis', + 'Swimming', + 'Running', + 'Cycling', + 'Badminton', + 'Yoga', + 'Gym & Fitness', - "Music", - "Dancing", - "Photography", - "Painting", - "Gaming", - "Reading", - "Writing", - "DIY & Crafting", - "Cooking", + 'Music', + 'Dancing', + 'Photography', + 'Painting', + 'Gaming', + 'Reading', + 'Writing', + 'DIY & Crafting', + 'Cooking', - "Extrovert", - "Introvert", - "Night Owl", - "Early Bird", + 'Extrovert', + 'Introvert', + 'Night Owl', + 'Early Bird', - "Traveler", - "Backpacker", - "Nature Lover", - "Camping", - "Beach Lover", + 'Traveler', + 'Backpacker', + 'Nature Lover', + 'Camping', + 'Beach Lover', - "Dog Lover", - "Cat Lover", + 'Dog Lover', + 'Cat Lover', - "Freelancer", - "Entrepreneur", - "Office Worker", - "Remote Worker", - "Student", - "Self-Employed"); + 'Freelancer', + 'Entrepreneur', + 'Office Worker', + 'Remote Worker', + 'Student', + 'Self-Employed' + ); END IF; END $$; ` diff --git a/internal/handlers/user_handler_update_user_information.go b/internal/handlers/user_handler_update_user_information.go index 6041308..be2b206 100644 --- a/internal/handlers/user_handler_update_user_information.go +++ b/internal/handlers/user_handler_update_user_information.go @@ -39,7 +39,10 @@ func (h *UserHandler) UpdateUserInformation(c *fiber.Ctx) error { } validate := validator.New() - validate.RegisterValidation("lifestyle", utils.ValidateLifestyles) + lifestyle_err := validate.RegisterValidation("lifestyle", utils.ValidateLifestyles) + if lifestyle_err != nil { + return errorHandler.BadRequestError(err, "your lifestyle-tg is incorrect format") + } if err := validate.Struct(requestBody); err != nil { return errorHandler.BadRequestError(err, "your request body is incorrect")