Enhance Customer Module with Validation and Username Handling

- Introduced a new ValidationService for customer operations to ensure valid username formats.
- Updated customer service and handler to utilize the new validation service, enhancing input validation during registration and updates.
- Modified CreateCustomerForm and UpdateCustomerForm to enforce username validation rules.
- Adjusted repository indexing to improve search efficiency by refining the text index for customer data.
- Ensured consistent handling of usernames across the customer module, improving overall data integrity and user experience.
This commit is contained in:
n.nakhostin
2025-09-24 12:57:23 +03:30
parent 080c1d18e5
commit 939f940499
6 changed files with 65 additions and 9 deletions
+3 -3
View File
@@ -13,7 +13,7 @@ type CreateCustomerForm struct {
Type string `json:"type" valid:"required,in(individual|company|government)" example:"individual"`
Role string `json:"role" valid:"required,in(admin|analyst)" example:"analyst"`
Companies []string `json:"companies,omitempty" valid:"optional"`
Username string `json:"username" valid:"required,alphanum,length(3|30)" example:"user"`
Username string `json:"username" valid:"required,username,length(3|30)" example:"user"`
Email string `json:"email" valid:"required,email" example:"user@opplens.com"`
Password string `json:"password" valid:"required,length(8|128)" example:"User!1234"`
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"`
@@ -25,7 +25,7 @@ type UpdateCustomerForm struct {
Type string `json:"type" valid:"required,in(individual|company|government)" example:"individual"`
Role string `json:"role" valid:"required,in(admin|analyst)" example:"analyst"`
Companies []string `json:"companies,omitempty" valid:"optional"`
Username string `json:"username" valid:"required,alphanum,length(3|30)" example:"user"`
Username string `json:"username" valid:"required,username,length(3|30)" example:"user"`
Email string `json:"email" valid:"required,email" example:"user@opplens.com"`
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"`
}
@@ -132,7 +132,7 @@ type ChangePasswordForm struct {
type UpdateProfileForm struct {
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)"`
Username *string `json:"username,omitempty" valid:"optional,alphanum,length(3|30)"`
Username *string `json:"username,omitempty" valid:"optional,username,length(3|30)"`
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
}
+3 -1
View File
@@ -15,15 +15,17 @@ type Handler struct {
authService authorization.AuthorizationService
userHandler *user.Handler
logger logger.Logger
validator ValidationService
}
// NewHandler creates a new customer handler
func NewHandler(service Service, userHandler *user.Handler, authService authorization.AuthorizationService, logger logger.Logger) *Handler {
func NewHandler(service Service, userHandler *user.Handler, authService authorization.AuthorizationService, logger logger.Logger, validator ValidationService) *Handler {
return &Handler{
service: service,
authService: authService,
userHandler: userHandler,
logger: logger,
validator: validator,
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Re
// Create indexes using the ORM's index management
indexes := []orm.Index{
*orm.CreateUniqueIndex("username_idx", bson.D{{Key: "username", Value: 1}}),
*orm.CreateTextIndex("search_idx", "full_name", "email", "username", "phone"),
*orm.CreateTextIndex("search_idx", "email", "username"),
}
// Create indexes
+13 -1
View File
@@ -60,10 +60,11 @@ type customerService struct {
companyService company.Service
redisClient redis.Client
notification notification.SDK
validator ValidationService
}
// New creates a new customer service
func NewService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, companyService company.Service, redisClient redis.Client, notificationSDK notification.SDK) Service {
func NewService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, companyService company.Service, redisClient redis.Client, notificationSDK notification.SDK, validator ValidationService) Service {
return &customerService{
repository: repository,
logger: logger,
@@ -71,6 +72,7 @@ func NewService(repository Repository, logger logger.Logger, authService authori
companyService: companyService,
redisClient: redisClient,
notification: notificationSDK,
validator: validator,
}
}
@@ -82,6 +84,11 @@ func (s *customerService) Register(ctx context.Context, form *CreateCustomerForm
return nil, errors.New("customer with this email already exists")
}
// Check validator
if !s.validator.IsValidUsername(form.Username) {
return nil, errors.New("invalid username format")
}
// Verify that all company IDs exist
var Companies []string
if len(form.Companies) > 0 {
@@ -307,6 +314,11 @@ func (s *customerService) Update(ctx context.Context, id string, form *UpdateCus
customer.Email = form.Email
}
// Check validator
if !s.validator.IsValidUsername(form.Username) {
return nil, errors.New("invalid username format")
}
// Check if username already exists (if changing username)
if form.Username != "" && strings.ToLower(form.Username) != customer.Username {
existingCustomer, _ := s.repository.GetByUsername(ctx, strings.ToLower(form.Username))
+41
View File
@@ -0,0 +1,41 @@
package customer
import (
"regexp"
"github.com/asaskevich/govalidator"
)
// ValidationService defines validation methods for customer operations
type ValidationService interface {
IsValidUsername(username string) bool
}
// validationService implements the ValidationService interface
type validationService struct {
usernameRegex *regexp.Regexp
}
// NewValidationService creates a new validation service
func NewValidationService() ValidationService {
// Compile the username regex pattern
usernameRegex := regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9._]*[a-zA-Z0-9])?$`)
// Register custom validator with govalidator
govalidator.CustomTypeTagMap.Set("username", govalidator.CustomTypeValidator(func(i interface{}, o interface{}) bool {
username, ok := i.(string)
if !ok {
return false
}
return usernameRegex.MatchString(username)
}))
return &validationService{
usernameRegex: usernameRegex,
}
}
// IsValidUsername checks if a username matches the required pattern
func (v *validationService) IsValidUsername(username string) bool {
return v.usernameRegex.MatchString(username)
}