diff --git a/.env b/.env index 62b7898..7c56a48 100644 --- a/.env +++ b/.env @@ -7,3 +7,5 @@ POSTGRES_PASSWORD="golang_api_users" POSTGRES_HOST="localhost" POSTGRES_PORT="5432" DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable" + +VIA_CEP_URL="https://viacep.com.br/ws" diff --git a/api/viacep/viacep.go b/api/viacep/viacep.go new file mode 100644 index 0000000..fe59668 --- /dev/null +++ b/api/viacep/viacep.go @@ -0,0 +1,42 @@ +package viacep + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/wiliamvj/api-users-golang/config/env" +) + +type ViaCepResponse struct { + CEP string `json:"cep"` + Logradouro string `json:"logradouro"` + Complemento string `json:"complemento"` + Bairro string `json:"bairro"` + Localidade string `json:"localidade"` + UF string `json:"uf"` + IBGE string `json:"ibge"` + GIA string `json:"gia"` + DDD string `json:"ddd"` + SIAFI string `json:"siafi"` +} + +func GetCep(cep string) (*ViaCepResponse, error) { + url := fmt.Sprintf("%s/%s/json", env.Env.ViaCepURL, cep) + var viaCepResponse ViaCepResponse + + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(&viaCepResponse) + if err != nil { + return nil, err + } + if viaCepResponse.CEP == "" { + return nil, fmt.Errorf("cep not found") + } + return &viaCepResponse, nil +} diff --git a/config/env/env.go b/config/env/env.go index 8f5801e..3ca3f62 100644 --- a/config/env/env.go +++ b/config/env/env.go @@ -10,6 +10,7 @@ type config struct { GoEnv string `mapstructure:"GO_ENV"` GoPort string `mapstructure:"GO_PORT"` DatabaseURL string `mapstructure:"DATABASE_URL"` + ViaCepURL string `mapstructure:"VIA_CEP_URL"` } func LoadingConfig(path string) (*config, error) { diff --git a/go.mod b/go.mod index 79b0a85..f04da13 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/spf13/viper v1.17.0 github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/swag v1.8.1 + golang.org/x/crypto v0.13.0 ) require ( @@ -39,7 +40,6 @@ require ( github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.15.0 // indirect golang.org/x/sys v0.12.0 // indirect diff --git a/http_client.http b/http_client.http index 9428090..227fee5 100644 --- a/http_client.http +++ b/http_client.http @@ -6,7 +6,8 @@ content-type: application/json { "name": "John Doe", "email": "john.doe@email.com", - "password": "12345678@" + "password": "12345678@", + "cep": "77132243" } ### @@ -17,7 +18,8 @@ content-type: application/json { "name": "John Doe", - "email": "john.doe@email.com" + "email": "john.doe@email.com", + "cep": "00000000" } ### diff --git a/internal/dto/user_dto.go b/internal/dto/user_dto.go index 75d07ac..d6ed5c1 100644 --- a/internal/dto/user_dto.go +++ b/internal/dto/user_dto.go @@ -4,11 +4,13 @@ type CreateUserDto struct { Name string `json:"name" validate:"required,min=3,max=30"` Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"required,min=8,max=30,containsany=!@#$%*"` + CEP string `json:"cep" validate:"required,min=8,max=8"` } type UpdateUserDto struct { Name string `json:"name" validate:"omitempty,min=3,max=30"` Email string `json:"email" validate:"omitempty,email"` + CEP string `json:"cep" validate:"omitempty,min=8,max=8"` } type UpdateUserPasswordDto struct { diff --git a/internal/entity/user_entity.go b/internal/entity/user_entity.go index 91c5ede..c06ac13 100644 --- a/internal/entity/user_entity.go +++ b/internal/entity/user_entity.go @@ -3,10 +3,20 @@ package entity import "time" type UserEntity struct { - ID string `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Password string `json:"password,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Password string `json:"password,omitempty"` + Address UserAddress `json:"address,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type UserAddress struct { + CEP string `json:"cep"` + IBGE string `json:"ibge"` + UF string `json:"uf"` + City string `json:"city"` + Complement string `json:"complement,omitempty"` + Street string `json:"street"` } diff --git a/internal/handler/userhandler/user_handler.go b/internal/handler/userhandler/user_handler.go index 3d3a557..02dc2f6 100644 --- a/internal/handler/userhandler/user_handler.go +++ b/internal/handler/userhandler/user_handler.go @@ -36,7 +36,7 @@ func (h *handler) CreateUser(w http.ResponseWriter, r *http.Request) { } err := json.NewDecoder(r.Body).Decode(&req) if err != nil { - slog.Error("error to decode body", "err", err, slog.String("package", "handler_user")) + slog.Error("error to decode body", "err", err, slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to decode body") json.NewEncoder(w).Encode(msg) @@ -44,14 +44,20 @@ func (h *handler) CreateUser(w http.ResponseWriter, r *http.Request) { } httpErr := validation.ValidateHttpData(req) if httpErr != nil { - slog.Error(fmt.Sprintf("error to validate data: %v", httpErr), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to validate data: %v", httpErr), slog.String("package", "userhandler")) w.WriteHeader(httpErr.Code) json.NewEncoder(w).Encode(httpErr) return } err = h.service.CreateUser(r.Context(), req) if err != nil { - slog.Error(fmt.Sprintf("error to create user: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to create user: %v", err), slog.String("package", "userhandler")) + if err.Error() == "cep not found" { + w.WriteHeader(http.StatusNotFound) + msg := httperr.NewNotFoundError("cep not found") + json.NewEncoder(w).Encode(msg) + return + } w.WriteHeader(http.StatusInternalServerError) msg := httperr.NewBadRequestError("error to create user") json.NewEncoder(w).Encode(msg) @@ -85,7 +91,7 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) { } _, err := uuid.Parse(id) if err != nil { - slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to parse id") json.NewEncoder(w).Encode(msg) @@ -100,7 +106,7 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) { } err = json.NewDecoder(r.Body).Decode(&req) if err != nil { - slog.Error("error to decode body", "err", err, slog.String("package", "handler_user")) + slog.Error("error to decode body", "err", err, slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to decode body") json.NewEncoder(w).Encode(msg) @@ -108,20 +114,26 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) { } httpErr := validation.ValidateHttpData(req) if httpErr != nil { - slog.Error(fmt.Sprintf("error to validate data: %v", httpErr), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to validate data: %v", httpErr), slog.String("package", "userhandler")) w.WriteHeader(httpErr.Code) json.NewEncoder(w).Encode(httpErr) return } err = h.service.UpdateUser(r.Context(), req, id) if err != nil { - slog.Error(fmt.Sprintf("error to update user: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to update user: %v", err), slog.String("package", "userhandler")) if err.Error() == "user not found" { w.WriteHeader(http.StatusNotFound) msg := httperr.NewNotFoundError("user not found") json.NewEncoder(w).Encode(msg) return } + if err.Error() == "cep not found" { + w.WriteHeader(http.StatusNotFound) + msg := httperr.NewNotFoundError("cep not found") + json.NewEncoder(w).Encode(msg) + return + } w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(err) return @@ -152,7 +164,7 @@ func (h *handler) GetUserByID(w http.ResponseWriter, r *http.Request) { } _, err := uuid.Parse(id) if err != nil { - slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to parse id") json.NewEncoder(w).Encode(msg) @@ -160,7 +172,7 @@ func (h *handler) GetUserByID(w http.ResponseWriter, r *http.Request) { } res, err := h.service.GetUserByID(r.Context(), id) if err != nil { - slog.Error(fmt.Sprintf("error to get user: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to get user: %v", err), slog.String("package", "userhandler")) if err.Error() == "user not found" { w.WriteHeader(http.StatusNotFound) msg := httperr.NewNotFoundError("user not found") @@ -201,7 +213,7 @@ func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) { } _, err := uuid.Parse(id) if err != nil { - slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to parse id") json.NewEncoder(w).Encode(msg) @@ -209,7 +221,7 @@ func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) { } err = h.service.DeleteUser(r.Context(), id) if err != nil { - slog.Error(fmt.Sprintf("error to delete user: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to delete user: %v", err), slog.String("package", "userhandler")) if err.Error() == "user not found" { w.WriteHeader(http.StatusNotFound) msg := httperr.NewNotFoundError("user not found") @@ -239,7 +251,7 @@ func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) { func (h *handler) FindManyUsers(w http.ResponseWriter, r *http.Request) { res, err := h.service.FindManyUsers(r.Context()) if err != nil { - slog.Error(fmt.Sprintf("error to find many users: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to find many users: %v", err), slog.String("package", "userhandler")) w.WriteHeader(http.StatusInternalServerError) msg := httperr.NewBadRequestError("error to find many users") json.NewEncoder(w).Encode(msg) @@ -276,7 +288,7 @@ func (h *handler) UpdateUserPassword(w http.ResponseWriter, r *http.Request) { } _, err := uuid.Parse(id) if err != nil { - slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to parse id: %v", err), slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to parse id") json.NewEncoder(w).Encode(msg) @@ -291,7 +303,7 @@ func (h *handler) UpdateUserPassword(w http.ResponseWriter, r *http.Request) { } err = json.NewDecoder(r.Body).Decode(&req) if err != nil { - slog.Error("error to decode body", "err", err, slog.String("package", "handler_user")) + slog.Error("error to decode body", "err", err, slog.String("package", "userhandler")) w.WriteHeader(http.StatusBadRequest) msg := httperr.NewBadRequestError("error to decode body") json.NewEncoder(w).Encode(msg) @@ -299,14 +311,14 @@ func (h *handler) UpdateUserPassword(w http.ResponseWriter, r *http.Request) { } httpErr := validation.ValidateHttpData(req) if httpErr != nil { - slog.Error(fmt.Sprintf("error to validate data: %v", httpErr), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to validate data: %v", httpErr), slog.String("package", "userhandler")) w.WriteHeader(httpErr.Code) json.NewEncoder(w).Encode(httpErr) return } err = h.service.UpdateUserPassword(r.Context(), &req, id) if err != nil { - slog.Error(fmt.Sprintf("error to update user password: %v", err), slog.String("package", "handler_user")) + slog.Error(fmt.Sprintf("error to update user password: %v", err), slog.String("package", "userhandler")) if err.Error() == "user not found" { w.WriteHeader(http.StatusNotFound) msg := httperr.NewNotFoundError("user not found") diff --git a/internal/service/userservice/user_service.go b/internal/service/userservice/user_service.go index cdd0f6c..6f6bb80 100644 --- a/internal/service/userservice/user_service.go +++ b/internal/service/userservice/user_service.go @@ -7,6 +7,7 @@ import ( "time" "github.com/google/uuid" + "github.com/wiliamvj/api-users-golang/api/viacep" "github.com/wiliamvj/api-users-golang/internal/dto" "github.com/wiliamvj/api-users-golang/internal/entity" "github.com/wiliamvj/api-users-golang/internal/handler/response" @@ -23,16 +24,29 @@ func (s *service) CreateUser(ctx context.Context, u dto.CreateUserDto) error { slog.Error("user already exists", slog.String("package", "userservice")) return errors.New("user already exists") } - passwordEncrypted, err := bcrypt.GenerateFromPassword([]byte(u.Password), 31) + passwordEncrypted, err := bcrypt.GenerateFromPassword([]byte(u.Password), 12) if err != nil { slog.Error("error to encrypt password", "err", err, slog.String("package", "userservice")) return errors.New("error to encrypt password") } + cep, err := viacep.GetCep(u.CEP) + if err != nil { + slog.Error("error to get cep", "err", err, slog.String("package", "userservice")) + return err + } newUser := entity.UserEntity{ - ID: uuid.New().String(), - Name: u.Name, - Email: u.Email, - Password: string(passwordEncrypted), + ID: uuid.New().String(), + Name: u.Name, + Email: u.Email, + Password: string(passwordEncrypted), + Address: entity.UserAddress{ + CEP: cep.CEP, + IBGE: cep.IBGE, + UF: cep.UF, + City: cep.Localidade, + Complement: cep.Complemento, + Street: cep.Logradouro, + }, CreatedAt: time.Now(), UpdatedAt: time.Now(), } @@ -54,6 +68,7 @@ func (s *service) UpdateUser(ctx context.Context, u dto.UpdateUserDto, id string slog.Error("user not found", slog.String("package", "userservice")) return errors.New("user already exists") } + var updateUser entity.UserEntity if u.Email != "" { verifyUserEmail, err := s.repo.FindUserByEmail(ctx, u.Email) if err != nil { @@ -64,13 +79,26 @@ func (s *service) UpdateUser(ctx context.Context, u dto.UpdateUserDto, id string slog.Error("user already exists", slog.String("package", "userservice")) return errors.New("user already exists") } + updateUser.Email = u.Email } - updateUser := entity.UserEntity{ - ID: id, - Name: u.Name, - Email: u.Email, - UpdatedAt: time.Now(), + if u.CEP != "" { + cep, err := viacep.GetCep(u.CEP) + if err != nil { + slog.Error("error to get cep", "err", err, slog.String("package", "userservice")) + return err + } + updateUser.Address = entity.UserAddress{ + CEP: cep.CEP, + IBGE: cep.IBGE, + UF: cep.UF, + City: cep.Localidade, + Complement: cep.Complemento, + Street: cep.Logradouro, + } } + updateUser.ID = id + updateUser.Name = u.Name + updateUser.UpdatedAt = time.Now() err = s.repo.UpdateUser(ctx, &updateUser) if err != nil { slog.Error("error to update user", "err", err, slog.String("package", "userservice"))