From 9db570bebad820b5de01562f75d1416892fc392c Mon Sep 17 00:00:00 2001 From: le-xuan-quynh Date: Wed, 4 May 2022 17:40:31 +0700 Subject: [PATCH] add match lover and un-match lover API --- app.env.example | 4 +- cmd/authorization/auth.go | 34 ++++- internal/config.go | 1 + internal/database/postgres-repository.go | 92 +++++++++++++ internal/database/repository.go | 10 ++ internal/database/verification-data.go | 19 +++ internal/errors.go | 15 +++ pkg/authorization/endpoints/endpoints.go | 69 ++++++++++ pkg/authorization/reqresponse.go | 22 +++ pkg/authorization/service.go | 3 + pkg/authorization/transport/http.go | 76 +++++++++++ pkg/authorization/users-service.go | 162 +++++++++++++++++++++++ 12 files changed, 505 insertions(+), 2 deletions(-) diff --git a/app.env.example b/app.env.example index fe4f4e9..601f69a 100644 --- a/app.env.example +++ b/app.env.example @@ -13,6 +13,7 @@ JWT_EXPIRATION=30 SENDGRID_API_KEY= MAIL_VERIFICATION_CODE_EXPIRATION=24 PASSWORD_RESET_CODE_EXPIRATION=15 +MATCH_CODE_EXPIRATION=15 MAIL_VERIFICATION_TEMPLATE_ID= PASSWORD_RESET_TEMPLATE_ID= MAIL_SENDER=yourmail@example.com @@ -20,5 +21,6 @@ ISSUER=codetoanbug.auth.service HTTP_PORT=8081 MAIL_TITLE="Love Letter Verification" CHANGE_PASSWORD_LIMIT=10 -SEND_MAIL_LIMIT=10 +SEND_MAIL_VERIFY_LIMIT=1 +SEND_MAIL_RESET_PASSWORD_LIMIT=1 LOGIN_LIMIT=10 \ No newline at end of file diff --git a/cmd/authorization/auth.go b/cmd/authorization/auth.go index 846633b..49bb08e 100644 --- a/cmd/authorization/auth.go +++ b/cmd/authorization/auth.go @@ -74,7 +74,7 @@ const profileSchema = ` ) ` -// schema for securityuser table +// schema for security user table const securityUserSchema = ` create table if not exists passworusers ( id Varchar(36) not null, @@ -105,6 +105,35 @@ const limitSchema = ` ) ` +// schema for match love table +const matchLoveSchema = ` + create table if not exists matchloves ( + id Varchar(36) not null, + userid Varchar(36) not null, + matchid Varchar(36) not null, + createdat Timestamp not null, + updatedat Timestamp not null, + Primary Key (id), + Constraint fk_user_id Foreign Key(userid) References users(id) + On Delete Cascade On Update Cascade + ) +` + +// schema for generate match code table +const generateMatchCodeSchema = ` + create table if not exists generatematchcodes ( + id Varchar(36) not null, + userid Varchar(36) not null, + code Varchar(10) not null, + expiresat Timestamp not null, + createdat Timestamp not null, + updatedat Timestamp not null, + Primary Key (id), + Constraint fk_user_id Foreign Key(userid) References users(id) + On Delete Cascade On Update Cascade + ) +` + func main() { logger := utils.NewLogger() @@ -129,6 +158,9 @@ func main() { db.MustExec(profileSchema) db.MustExec(securityUserSchema) db.MustExec(limitSchema) + db.MustExec(matchLoveSchema) + db.MustExec(generateMatchCodeSchema) + logger.Info("database created") // repository contains all the methods that interact with DB to perform CURD operations for user. repository := database.NewPostgresRepository(db, logger) // mailService contains the utility methods to send an email diff --git a/internal/config.go b/internal/config.go index 550e11b..da53db4 100644 --- a/internal/config.go +++ b/internal/config.go @@ -23,6 +23,7 @@ type Configurations struct { SendGridApiKey string `mapstructure:"SENDGRID_API_KEY"` MailVerifCodeExpiration int `mapstructure:"MAIL_VERIFICATION_CODE_EXPIRATION"` // in hours PassResetCodeExpiration int `mapstructure:"PASSWORD_RESET_CODE_EXPIRATION"` // in minutes + MatchCodeExpiration int `mapstructure:"MATCH_CODE_EXPIRATION"` // in minutes MailVerifTemplateID string `mapstructure:"MAIL_VERIFICATION_TEMPLATE_ID"` PassResetTemplateID string `mapstructure:"PASSWORD_RESET_TEMPLATE_ID"` MailSender string `mapstructure:"MAIL_SENDER"` diff --git a/internal/database/postgres-repository.go b/internal/database/postgres-repository.go index 45833da..1fe496c 100644 --- a/internal/database/postgres-repository.go +++ b/internal/database/postgres-repository.go @@ -318,3 +318,95 @@ func (repo *postgresRepository) ClearLimitData(ctx context.Context, limitType Li _, err := repo.db.ExecContext(ctx, query) return err } + +// GetMatchVerifyDataByCode returns the match data +func (repo *postgresRepository) GetMatchVerifyDataByCode(ctx context.Context, code string) (*MatchVerifyData, error) { + query := "select * from generatematchcodes where code = $1" + matchData := &MatchVerifyData{} + err := repo.db.GetContext(ctx, matchData, query, code) + return matchData, err +} + +// InsertOrUpdateMatchVerifyData updates the match data +func (repo *postgresRepository) InsertOrUpdateMatchVerifyData(ctx context.Context, matchData *MatchVerifyData) error { + // Check exist or not match data + _, err := repo.GetMatchVerifyDataByCode(ctx, matchData.Code) + isInsert := false + if err != nil { + isInsert = true + } + matchData.ID = uuid.NewV4().String() + matchData.CreatedAt = time.Now() + matchData.UpdatedAt = time.Now() + // Insert or update + if isInsert { + // Insert the match data + query := "insert into generatematchcodes(id, userid, code, expiresat, createdat, updatedat) values($1, $2, $3, $4, $5, $6)" + _, err := repo.db.ExecContext(ctx, query, + matchData.ID, + matchData.UserID, + matchData.Code, + matchData.ExpiresAt, + matchData.CreatedAt, + matchData.UpdatedAt) + return err + } else { + // Update the match data + query := "update generatematchcodes set code = $1, expiresat = $2, updatedat = $3 where userid = $4" + _, err := repo.db.ExecContext(ctx, query, + matchData.Code, + matchData.ExpiresAt, + matchData.UpdatedAt, + matchData.UserID) + return err + } +} + +// DeleteMatchVerifyDataByUserID deletes the match data +func (repo *postgresRepository) DeleteMatchVerifyDataByUserID(ctx context.Context, userID string) error { + query := "delete from generatematchcodes where userid = $1" + _, err := repo.db.ExecContext(ctx, query, userID) + return err +} + +// GetMatchLoveDataByUserID returns the match love data +func (repo *postgresRepository) GetMatchLoveDataByUserID(ctx context.Context, userID string) (*MatchLoveData, error) { + query := "select * from matchloves where userid = $1" + matchData := &MatchLoveData{} + err := repo.db.GetContext(ctx, matchData, query, userID) + return matchData, err +} + +// InsertOrUpdateMatchLoveData Insert or update match love data +func (repo *postgresRepository) InsertOrUpdateMatchLoveData(ctx context.Context, matchData *MatchLoveData) error { + // Check exist or not match data + _, err := repo.GetMatchLoveDataByUserID(ctx, matchData.UserID) + isInsert := false + if err != nil { + isInsert = true + + } + matchData.ID = uuid.NewV4().String() + matchData.CreatedAt = time.Now() + matchData.UpdatedAt = time.Now() + // Insert or update + if isInsert { + // Insert the match data + query := "insert into matchloves(id, userid, matchid, createdat, updatedat) values($1, $2, $3, $4, $5)" + _, err := repo.db.ExecContext(ctx, query, + matchData.ID, + matchData.UserID, + matchData.MatchID, + matchData.CreatedAt, + matchData.UpdatedAt) + return err + } else { + // Update the match data + query := "update matchloves set matchid = $1, updatedat = $2 where userid = $3" + _, err := repo.db.ExecContext(ctx, query, + matchData.MatchID, + matchData.UpdatedAt, + matchData.UserID) + return err + } +} diff --git a/internal/database/repository.go b/internal/database/repository.go index c00d43a..a97a371 100644 --- a/internal/database/repository.go +++ b/internal/database/repository.go @@ -41,4 +41,14 @@ type UserRepository interface { InsertOrUpdateLimitData(ctx context.Context, limitData *LimitData, limitType LimitType) error // ClearLimitData Clear limit data ClearLimitData(ctx context.Context, limitType LimitType) error + // GetMatchVerifyDataByCode Get match data + GetMatchVerifyDataByCode(ctx context.Context, userID string) (*MatchVerifyData, error) + // InsertOrUpdateMatchVerifyData Insert or update match data + InsertOrUpdateMatchVerifyData(ctx context.Context, matchData *MatchVerifyData) error + // DeleteMatchVerifyDataByUserID Delete match data + DeleteMatchVerifyDataByUserID(ctx context.Context, userID string) error + // GetMatchLoveDataByUserID Get match love data + GetMatchLoveDataByUserID(ctx context.Context, userID string) (*MatchLoveData, error) + // InsertOrUpdateMatchLoveData Insert or update match love data + InsertOrUpdateMatchLoveData(ctx context.Context, matchData *MatchLoveData) error } diff --git a/internal/database/verification-data.go b/internal/database/verification-data.go index cfe5873..4cea8bb 100644 --- a/internal/database/verification-data.go +++ b/internal/database/verification-data.go @@ -33,3 +33,22 @@ type VerificationData struct { ExpiresAt time.Time `json:"expiresat" sql:"expiresat"` Type VerificationDataType `json:"type" sql:"type"` } + +// MatchVerifyData represents the type for the data stored for matching. +type MatchVerifyData struct { + ID string `json:"id"` + UserID string `json:"userid" sql:"userid"` + Code string `json:"code" validate:"required" sql:"code"` + ExpiresAt time.Time `json:"expiresat" sql:"expiresat"` + CreatedAt time.Time `json:"createdat" sql:"createdat"` + UpdatedAt time.Time `json:"updatedat" sql:"updatedat"` +} + +// MatchLoveData represents the type for the data stored for matching. +type MatchLoveData struct { + ID string `json:"id"` + UserID string `json:"userid" sql:"userid"` + MatchID string `json:"matchid" sql:"matchid"` + CreatedAt time.Time `json:"createdat" sql:"createdat"` + UpdatedAt time.Time `json:"updatedat" sql:"updatedat"` +} diff --git a/internal/errors.go b/internal/errors.go index ccd10bc..2ca7642 100644 --- a/internal/errors.go +++ b/internal/errors.go @@ -40,6 +40,11 @@ const ( CodeInvalid = 20 UsernameRequired = 21 ExistUserName = 22 + MatchCodeIsExpired = 23 + MatchCodeIsIncorrect = 24 + UserAlreadyMatched = 25 + MatchCodeIsNotFound = 26 + UserNotMatch = 27 ) func (e ErrorResponse) Error() string { @@ -106,6 +111,16 @@ func (e ErrorResponse) Error() string { return "Username required" case ExistUserName: return "Username already exists" + case MatchCodeIsExpired: + return "match code is expired" + case MatchCodeIsIncorrect: + return "match code is incorrect" + case UserAlreadyMatched: + return "user already matched" + case MatchCodeIsNotFound: + return "match code is not found" + case UserNotMatch: + return "user not match" default: return "Unknown Error" } diff --git a/pkg/authorization/endpoints/endpoints.go b/pkg/authorization/endpoints/endpoints.go index 80ace5d..e6b120c 100644 --- a/pkg/authorization/endpoints/endpoints.go +++ b/pkg/authorization/endpoints/endpoints.go @@ -27,6 +27,9 @@ type Set struct { ResetPasswordEndpoint endpoint.Endpoint GenerateAccessTokenEndpoint endpoint.Endpoint GetVerifyMailCodeEndpoint endpoint.Endpoint + GetMatchCodeEndpoint endpoint.Endpoint + MatchLoverEndpoint endpoint.Endpoint + UnMatchLoverEndpoint endpoint.Endpoint } func NewEndpointSet(svc authorization.Service, @@ -98,6 +101,21 @@ func NewEndpointSet(svc authorization.Service, getVerifyMailCodeEndpoint = middleware.ValidateParamRequest(validator, logger)(getVerifyMailCodeEndpoint) getVerifyMailCodeEndpoint = middleware.ValidateAccessToken(auth, logger)(getVerifyMailCodeEndpoint) + getMatchCodeEndpoint := MakeGetMatchCodeEndpoint(svc) + getMatchCodeEndpoint = middleware.RateLimitRequest(tb, logger)(getMatchCodeEndpoint) + getMatchCodeEndpoint = middleware.ValidateParamRequest(validator, logger)(getMatchCodeEndpoint) + getMatchCodeEndpoint = middleware.ValidateAccessToken(auth, logger)(getMatchCodeEndpoint) + + matchLoverEndpoint := MakeMatchLoverEndpoint(svc) + matchLoverEndpoint = middleware.RateLimitRequest(tb, logger)(matchLoverEndpoint) + matchLoverEndpoint = middleware.ValidateParamRequest(validator, logger)(matchLoverEndpoint) + matchLoverEndpoint = middleware.ValidateAccessToken(auth, logger)(matchLoverEndpoint) + + unMatchLoverEndpoint := MakeUnMatchedLoverEndpoint(svc) + unMatchLoverEndpoint = middleware.RateLimitRequest(tb, logger)(unMatchLoverEndpoint) + unMatchLoverEndpoint = middleware.ValidateParamRequest(validator, logger)(unMatchLoverEndpoint) + unMatchLoverEndpoint = middleware.ValidateAccessToken(auth, logger)(unMatchLoverEndpoint) + return Set{ HealthCheckEndpoint: healthCheckEndpoint, RegisterEndpoint: registerEndpoint, @@ -113,6 +131,9 @@ func NewEndpointSet(svc authorization.Service, ResetPasswordEndpoint: resetPasswordEndpoint, GenerateAccessTokenEndpoint: generateAccessTokenEndpoint, GetVerifyMailCodeEndpoint: getVerifyMailCodeEndpoint, + GetMatchCodeEndpoint: getMatchCodeEndpoint, + MatchLoverEndpoint: matchLoverEndpoint, + UnMatchLoverEndpoint: unMatchLoverEndpoint, } } @@ -350,3 +371,51 @@ func MakeGetVerifyMailCodeEndpoint(svc authorization.Service) endpoint.Endpoint return "Email has been successfully verified", nil } } + +// MakeGetMatchCodeEndpoint returns an endpoint that invokes GetMatchCode on the service. +func MakeGetMatchCodeEndpoint(svc authorization.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + _, ok := request.(authorization.GetMatchCodeRequest) + if !ok { + cusErr := utils.NewErrorResponse(utils.BadRequest) + return nil, cusErr + } + matchCode, err := svc.GetMatchCode(ctx) + if err != nil { + return nil, err + } + return matchCode, nil + } +} + +// MakeMatchLoverEndpoint returns an endpoint that invokes MatchLover on the service. +func MakeMatchLoverEndpoint(svc authorization.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req, ok := request.(authorization.MatchLoverRequest) + if !ok { + cusErr := utils.NewErrorResponse(utils.BadRequest) + return nil, cusErr + } + err := svc.MatchLover(ctx, &req) + if err != nil { + return nil, err + } + return "successfully matched lover", nil + } +} + +// MakeUnMatchedLoverEndpoint returns an endpoint that invokes UnMatchedLover on the service. +func MakeUnMatchedLoverEndpoint(svc authorization.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + _, ok := request.(authorization.UnMatchLoverRequest) + if !ok { + cusErr := utils.NewErrorResponse(utils.BadRequest) + return nil, cusErr + } + err := svc.UnMatchedLover(ctx) + if err != nil { + return nil, err + } + return "successfully unmatched lover", nil + } +} diff --git a/pkg/authorization/reqresponse.go b/pkg/authorization/reqresponse.go index 9f5b676..dbd2134 100644 --- a/pkg/authorization/reqresponse.go +++ b/pkg/authorization/reqresponse.go @@ -143,3 +143,25 @@ type GenerateAccessResponse struct { type GetVerifyMailCodeRequest struct { AccessToken string `json:"access_token" validate:"required"` } + +// GetMatchCodeRequest is used to get match code +type GetMatchCodeRequest struct { + AccessToken string `json:"access_token" validate:"required"` +} + +// GetMatchCodeResponse is the response for get match code +type GetMatchCodeResponse struct { + Code string `json:"code"` + Message string `json:"message"` +} + +// MatchLoverRequest is used to match love +type MatchLoverRequest struct { + AccessToken string `json:"access_token" validate:"required"` + Code string `json:"code" validate:"required"` +} + +// UnMatchLoverRequest is used to unmatch love +type UnMatchLoverRequest struct { + AccessToken string `json:"access_token" validate:"required"` +} diff --git a/pkg/authorization/service.go b/pkg/authorization/service.go index 43e81dc..f96a71f 100644 --- a/pkg/authorization/service.go +++ b/pkg/authorization/service.go @@ -18,4 +18,7 @@ type Service interface { ResetPassword(ctx context.Context, request *CreateNewPasswordWithCodeRequest) error GenerateAccessToken(ctx context.Context) (interface{}, error) GetVerifyMailCode(ctx context.Context) error + GetMatchCode(ctx context.Context) (interface{}, error) + MatchLover(ctx context.Context, request *MatchLoverRequest) error + UnMatchedLover(ctx context.Context) error } diff --git a/pkg/authorization/transport/http.go b/pkg/authorization/transport/http.go index e65525a..1e3c87d 100644 --- a/pkg/authorization/transport/http.go +++ b/pkg/authorization/transport/http.go @@ -101,6 +101,24 @@ func NewHTTPHandler(ep endpoints.Set) http.Handler { encodeResponse, options..., )) + m.Handle("/get-match-code", httptransport.NewServer( + ep.GetMatchCodeEndpoint, + decodeHTTPGetMatchCodeRequest, + encodeResponse, + options..., + )) + m.Handle("/match-lover", httptransport.NewServer( + ep.MatchLoverEndpoint, + decodeHTTPMatchLoverRequest, + encodeResponse, + options..., + )) + m.Handle("/un-match-lover", httptransport.NewServer( + ep.UnMatchLoverEndpoint, + decodeHTTPUnMatchLoverRequest, + encodeResponse, + options..., + )) mux := http.NewServeMux() mux.Handle("/api/v1/", http.StripPrefix("/api/v1", m)) @@ -375,6 +393,64 @@ func decodeHTTPGetVerifyMailCodeRequest(_ context.Context, r *http.Request) (int } } +// decodeHTTPGetMatchCodeRequest decode request +func decodeHTTPGetMatchCodeRequest(_ context.Context, r *http.Request) (interface{}, error) { + if r.Method == "POST" { + var req authorization.GetMatchCodeRequest + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + return nil, utils.NewErrorResponse(utils.BadRequest) + } + if req.AccessToken == "" { + return nil, utils.NewErrorResponse(utils.AccessTokenRequired) + } + return req, nil + } else { + cusErr := utils.NewErrorResponse(utils.MethodNotAllowed) + return nil, cusErr + } +} + +// decodeHTTPMatchLoverRequest decode request +func decodeHTTPMatchLoverRequest(_ context.Context, r *http.Request) (interface{}, error) { + if r.Method == "POST" { + var req authorization.MatchLoverRequest + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + return nil, utils.NewErrorResponse(utils.BadRequest) + } + if req.AccessToken == "" { + return nil, utils.NewErrorResponse(utils.AccessTokenRequired) + } + if req.Code == "" { + return nil, utils.NewErrorResponse(utils.CodeRequired) + } + return req, nil + } else { + cusErr := utils.NewErrorResponse(utils.MethodNotAllowed) + return nil, cusErr + } +} + +// decodeHTTPUnMatchLoverRequest decode request +func decodeHTTPUnMatchLoverRequest(_ context.Context, r *http.Request) (interface{}, error) { + if r.Method == "POST" { + var req authorization.UnMatchLoverRequest + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + return nil, utils.NewErrorResponse(utils.BadRequest) + } + if req.AccessToken == "" { + return nil, utils.NewErrorResponse(utils.AccessTokenRequired) + } + return req, nil + } else { + cusErr := utils.NewErrorResponse(utils.MethodNotAllowed) + return nil, cusErr + } +} + +// encodeResponse encode response func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", "application/json; charset=utf-8") diff --git a/pkg/authorization/users-service.go b/pkg/authorization/users-service.go index 08e4915..f0513e5 100644 --- a/pkg/authorization/users-service.go +++ b/pkg/authorization/users-service.go @@ -7,6 +7,7 @@ import ( "context" "database/sql" "errors" + "fmt" "github.com/hashicorp/go-hclog" "golang.org/x/crypto/bcrypt" "strings" @@ -915,3 +916,164 @@ func (s *userService) GetVerifyMailCode(ctx context.Context) error { s.logger.Debug("successfully mailed password reset code") return nil } + +// GetMatchCode get match code +func (s *userService) GetMatchCode(ctx context.Context) (interface{}, error) { + userID, ok := ctx.Value(middleware.UserIDKey{}).(string) + if !ok { + s.logger.Error("Error getting userID from context") + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return nil, cusErr + } + user, err := s.repo.GetUserByID(ctx, userID) + if err != nil { + s.logger.Error("Cannot get user", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return nil, cusErr + } + // Check if user is banned + if user.Banned { + s.logger.Error("User is banned", "error", err) + cusErr := utils.NewErrorResponse(utils.Forbidden) + return nil, cusErr + } + // Generate match code + matchCode := utils.GenerateRandomString(8) + // Store match code to db + matchData := &database.MatchVerifyData{ + UserID: user.ID, + Code: matchCode, + ExpiresAt: time.Now().Add(time.Minute * time.Duration(s.configs.MatchCodeExpiration)), + } + err = s.repo.InsertOrUpdateMatchVerifyData(ctx, matchData) + if err != nil { + s.logger.Error("unable to store match data", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return nil, cusErr + } + s.logger.Debug("successfully generate & stored match code") + getMatchCodeResponse := &GetMatchCodeResponse{ + Code: matchCode, + Message: fmt.Sprintf("Successfully generated match code. Match code will expire in %d minute or when a new match code is generated.", s.configs.MatchCodeExpiration), + } + return getMatchCodeResponse, nil +} + +// MatchLover match love +func (s *userService) MatchLover(ctx context.Context, request *MatchLoverRequest) error { + userID, ok := ctx.Value(middleware.UserIDKey{}).(string) + if !ok { + s.logger.Error("Error getting userID from context") + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + user, err := s.repo.GetUserByID(ctx, userID) + if err != nil { + s.logger.Error("Cannot get user", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + // Check if user is banned + if user.Banned { + s.logger.Error("User is banned", "error", err) + cusErr := utils.NewErrorResponse(utils.Forbidden) + return cusErr + } + // Check if user has match code + matchData, err := s.repo.GetMatchVerifyDataByCode(ctx, request.Code) + if err != nil { + s.logger.Error("Cannot get match data", "error", err) + cusErr := utils.NewErrorResponse(utils.MatchCodeIsNotFound) + return cusErr + } + if matchData.Code == "" { + s.logger.Error("User does not have match code") + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + // Check if match code is expired + if matchData.ExpiresAt.Before(time.Now()) { + s.logger.Error("Match code is expired") + cusErr := utils.NewErrorResponse(utils.MatchCodeIsExpired) + return cusErr + } + // Check if match code is correct + if matchData.Code != request.Code { + s.logger.Error("Match code is incorrect") + cusErr := utils.NewErrorResponse(utils.MatchCodeIsIncorrect) + return cusErr + } + // Check if user has already matched + matchLove, err := s.repo.GetMatchLoveDataByUserID(ctx, user.ID) + if err != nil { + s.logger.Error("User not match", "error", err) + } + // Check if user has already matched + if matchLove.MatchID != "" { + s.logger.Error("User has already matched") + cusErr := utils.NewErrorResponse(utils.UserAlreadyMatched) + return cusErr + } + // Match user with new love + matchLove.UserID = user.ID + matchLove.MatchID = matchData.UserID + err = s.repo.InsertOrUpdateMatchLoveData(ctx, matchLove) + if err != nil { + s.logger.Error("Cannot insert or update match love data", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + // Delete match data + err = s.repo.DeleteMatchVerifyDataByUserID(ctx, matchData.UserID) + if err != nil { + s.logger.Error("Cannot delete match data", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + s.logger.Debug("Successfully matched user") + return nil +} + +// UnMatchedLover unmatched lover +func (s *userService) UnMatchedLover(ctx context.Context) error { + userID, ok := ctx.Value(middleware.UserIDKey{}).(string) + if !ok { + s.logger.Error("Error getting userID from context") + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + user, err := s.repo.GetUserByID(ctx, userID) + if err != nil { + s.logger.Error("Cannot get user", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + // Check if user is banned + if user.Banned { + s.logger.Error("User is banned", "error", err) + cusErr := utils.NewErrorResponse(utils.Forbidden) + return cusErr + } + // Check if user has match code + matchLove, err := s.repo.GetMatchLoveDataByUserID(ctx, user.ID) + if err != nil { + s.logger.Error("User does not match with someone", "error", err) + cusErr := utils.NewErrorResponse(utils.UserNotMatch) + return cusErr + } + if matchLove.MatchID == "" { + s.logger.Error("User does not match with someone") + cusErr := utils.NewErrorResponse(utils.UserNotMatch) + return cusErr + } + // Delete match data or update match data with code is empty + matchLove.MatchID = "" + err = s.repo.InsertOrUpdateMatchLoveData(ctx, matchLove) + if err != nil { + s.logger.Error("Cannot delete match love data", "error", err) + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + s.logger.Debug("Successfully unmatch lover") + return nil +}