Skip to content

Commit

Permalink
add match lover and un-match lover API
Browse files Browse the repository at this point in the history
  • Loading branch information
le-xuan-quynh committed May 4, 2022
1 parent 6247d09 commit 9db570b
Show file tree
Hide file tree
Showing 12 changed files with 505 additions and 2 deletions.
4 changes: 3 additions & 1 deletion app.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ JWT_EXPIRATION=30
SENDGRID_API_KEY=<get it from_https://app.sendgrid.com>
MAIL_VERIFICATION_CODE_EXPIRATION=24
PASSWORD_RESET_CODE_EXPIRATION=15
MATCH_CODE_EXPIRATION=15
MAIL_VERIFICATION_TEMPLATE_ID=<get it from https://mc.sendgrid.com/dynamic-templates>
PASSWORD_RESET_TEMPLATE_ID=<get it from https://mc.sendgrid.com/dynamic-templates>
[email protected]
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
34 changes: 33 additions & 1 deletion cmd/authorization/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()

Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
92 changes: 92 additions & 0 deletions internal/database/postgres-repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
10 changes: 10 additions & 0 deletions internal/database/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
19 changes: 19 additions & 0 deletions internal/database/verification-data.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
15 changes: 15 additions & 0 deletions internal/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"
}
Expand Down
69 changes: 69 additions & 0 deletions pkg/authorization/endpoints/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -113,6 +131,9 @@ func NewEndpointSet(svc authorization.Service,
ResetPasswordEndpoint: resetPasswordEndpoint,
GenerateAccessTokenEndpoint: generateAccessTokenEndpoint,
GetVerifyMailCodeEndpoint: getVerifyMailCodeEndpoint,
GetMatchCodeEndpoint: getMatchCodeEndpoint,
MatchLoverEndpoint: matchLoverEndpoint,
UnMatchLoverEndpoint: unMatchLoverEndpoint,
}
}

Expand Down Expand Up @@ -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
}
}
22 changes: 22 additions & 0 deletions pkg/authorization/reqresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
3 changes: 3 additions & 0 deletions pkg/authorization/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading

0 comments on commit 9db570b

Please sign in to comment.