diff --git a/cmd/authorization/auth.go b/cmd/authorization/auth.go index cd64c4e..7f3bcaa 100644 --- a/cmd/authorization/auth.go +++ b/cmd/authorization/auth.go @@ -131,14 +131,16 @@ const generateMatchCodeSchema = ` ) ` -// schema for mail table -const mailSchema = ` - create table if not exists mails ( +// schema for love letter table +const loveLetterSchema = ` + create table if not exists loveletter ( id Varchar(36) not null, userid Varchar(36) not null, - matchid Varchar(36) not null default '', + matchid Varchar(36) null, title Varchar(255) not null, - content Varchar(1000) not null, + body Varchar(32000) not null, + isread Boolean default false, + isdelete Boolean default false, timeopen Timestamp not null, createdat Timestamp not null, updatedat Timestamp not null, @@ -174,7 +176,7 @@ func main() { db.MustExec(limitSchema) db.MustExec(matchLoveSchema) db.MustExec(generateMatchCodeSchema) - db.MustExec(mailSchema) + db.MustExec(loveLetterSchema) logger.Info("database created") // repository contains all the methods that interact with DB to perform CURD operations for user. diff --git a/internal/database/love-letter.go b/internal/database/love-letter.go new file mode 100644 index 0000000..7a38930 --- /dev/null +++ b/internal/database/love-letter.go @@ -0,0 +1,17 @@ +package database + +import "time" + +// LoveLetter type for love letter +type LoveLetter struct { + ID string `json:"id" sql:"id"` + UserID string `json:"userid" sql:"userid"` + MatchID string `json:"matchid" sql:"matchid"` + Title string `json:"title" sql:"title"` + Body string `json:"body" sql:"body"` + IsRead bool `json:"isread" sql:"isread"` + IsDelete bool `json:"isdelete" sql:"isdelete"` + TimeOpen time.Time `json:"timeopen" sql:"timeopen"` + CreatedAt time.Time `json:"createdat" sql:"createdat"` + UpdatedAt time.Time `json:"updatedat" sql:"updatedat"` +} diff --git a/internal/database/postgres-repository.go b/internal/database/postgres-repository.go index 4b4fab4..b8d0a19 100644 --- a/internal/database/postgres-repository.go +++ b/internal/database/postgres-repository.go @@ -407,3 +407,25 @@ func (repo *postgresRepository) InsertOrDeleteMatchLoveData(ctx context.Context, return err } } + +// CreateLoveLetter creates a love letter +func (repo *postgresRepository) CreateLoveLetter(ctx context.Context, loveLetter *LoveLetter) error { + loveLetter.ID = uuid.NewV4().String() + loveLetter.TimeOpen = time.Now() + loveLetter.CreatedAt = time.Now() + loveLetter.UpdatedAt = time.Now() + query := "insert into loveletter(id, userid, matchid, title, body, isread, isdelete, timeopen, createdat, updatedat) " + + "values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" + _, err := repo.db.ExecContext(ctx, query, + loveLetter.ID, + loveLetter.UserID, + loveLetter.MatchID, + loveLetter.Title, + loveLetter.Body, + loveLetter.IsRead, + loveLetter.IsDelete, + loveLetter.TimeOpen, + loveLetter.CreatedAt, + loveLetter.UpdatedAt) + return err +} diff --git a/internal/database/repository.go b/internal/database/repository.go index 0eb7e75..d42f851 100644 --- a/internal/database/repository.go +++ b/internal/database/repository.go @@ -51,4 +51,6 @@ type UserRepository interface { GetMatchLoveDataByUserID(ctx context.Context, userID string) (*MatchLoveData, error) // InsertOrDeleteMatchLoveData Insert or update match love data InsertOrDeleteMatchLoveData(ctx context.Context, matchData *MatchLoveData, isDelete bool) error + // CreateLoveLetter Create love letter + CreateLoveLetter(ctx context.Context, loveLetter *LoveLetter) error } diff --git a/internal/errors.go b/internal/errors.go index d3f904f..8f33080 100644 --- a/internal/errors.go +++ b/internal/errors.go @@ -43,8 +43,11 @@ const ( MatchCodeIsExpired = 23 MatchCodeIsIncorrect = 24 UserAlreadyMatched = 25 - MatchCodeIsNotFound = 26 + MatchCodeIsNotExactly = 26 UserNotMatch = 27 + TitleRequired = 28 + BodyRequired = 29 + ValueTooLong = 30 ) func (e ErrorResponse) Error() string { @@ -117,10 +120,16 @@ func (e ErrorResponse) Error() string { return "match code is incorrect" case UserAlreadyMatched: return "user already matched" - case MatchCodeIsNotFound: + case MatchCodeIsNotExactly: return "match code is not exactly" case UserNotMatch: return "user not match" + case TitleRequired: + return "title required" + case BodyRequired: + return "body required" + case ValueTooLong: + return "value too long" default: return "Unknown Error" } diff --git a/pkg/authorization/endpoints/endpoints.go b/pkg/authorization/endpoints/endpoints.go index 3b7e17c..70294d3 100644 --- a/pkg/authorization/endpoints/endpoints.go +++ b/pkg/authorization/endpoints/endpoints.go @@ -31,6 +31,7 @@ type Set struct { MatchLoverEndpoint endpoint.Endpoint UnMatchLoverEndpoint endpoint.Endpoint GetMatchedLoverEndpoint endpoint.Endpoint + CreateLoveLetterEndpoint endpoint.Endpoint } func NewEndpointSet(svc authorization.Service, @@ -122,6 +123,11 @@ func NewEndpointSet(svc authorization.Service, getMatchedLoverEndpoint = middleware.ValidateParamRequest(validator, logger)(getMatchedLoverEndpoint) getMatchedLoverEndpoint = middleware.ValidateAccessToken(auth, logger)(getMatchedLoverEndpoint) + createLoveLetterEndpoint := MakeCreateLoveLetterEndpoint(svc) + createLoveLetterEndpoint = middleware.RateLimitRequest(tb, logger)(createLoveLetterEndpoint) + createLoveLetterEndpoint = middleware.ValidateParamRequest(validator, logger)(createLoveLetterEndpoint) + createLoveLetterEndpoint = middleware.ValidateAccessToken(auth, logger)(createLoveLetterEndpoint) + return Set{ HealthCheckEndpoint: healthCheckEndpoint, RegisterEndpoint: registerEndpoint, @@ -141,6 +147,7 @@ func NewEndpointSet(svc authorization.Service, MatchLoverEndpoint: matchLoverEndpoint, UnMatchLoverEndpoint: unMatchLoverEndpoint, GetMatchedLoverEndpoint: getMatchedLoverEndpoint, + CreateLoveLetterEndpoint: createLoveLetterEndpoint, } } @@ -442,3 +449,19 @@ func MakeGetMatchedLoverEndpoint(svc authorization.Service) endpoint.Endpoint { return matchLover, nil } } + +// MakeCreateLoveLetterEndpoint returns an endpoint that invokes CreateLoveLetter on the service. +func MakeCreateLoveLetterEndpoint(svc authorization.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req, ok := request.(authorization.CreateLoveLetterRequest) + if !ok { + cusErr := utils.NewErrorResponse(utils.BadRequest) + return nil, cusErr + } + err := svc.CreateLoveLetter(ctx, &req) + if err != nil { + return nil, err + } + return "successfully created love letter", nil + } +} diff --git a/pkg/authorization/reqresponse.go b/pkg/authorization/reqresponse.go index d193f41..f7a70df 100644 --- a/pkg/authorization/reqresponse.go +++ b/pkg/authorization/reqresponse.go @@ -178,3 +178,19 @@ type UnMatchLoverRequest struct { type GetMatchedLoverRequest struct { AccessToken string `json:"access_token" validate:"required"` } + +// CreateLoveLetterRequest is used to create love letter +type CreateLoveLetterRequest struct { + AccessToken string `json:"access_token" validate:"required"` + Title string `json:"title" validate:"required"` + Body string `json:"body" validate:"required"` +} + +//id Varchar(36) not null, +//userid Varchar(36) not null, +//matchid Varchar(36) null, +//title Varchar(255) not null, +//content Varchar(1000) not null, +//timeopen Timestamp not null, +//createdat Timestamp not null, +//updatedat Timestamp not null, diff --git a/pkg/authorization/service.go b/pkg/authorization/service.go index 137e3bc..7efc94e 100644 --- a/pkg/authorization/service.go +++ b/pkg/authorization/service.go @@ -22,4 +22,5 @@ type Service interface { MatchLover(ctx context.Context, request *MatchLoverRequest) error UnMatchedLover(ctx context.Context) error GetMatchLover(ctx context.Context) (interface{}, error) + CreateLoveLetter(ctx context.Context, request *CreateLoveLetterRequest) error } diff --git a/pkg/authorization/transport/http.go b/pkg/authorization/transport/http.go index 83e856f..afe3195 100644 --- a/pkg/authorization/transport/http.go +++ b/pkg/authorization/transport/http.go @@ -125,6 +125,12 @@ func NewHTTPHandler(ep endpoints.Set) http.Handler { encodeResponse, options..., )) + m.Handle("/create-love-letter", httptransport.NewServer( + ep.CreateLoveLetterEndpoint, + decodeHTTPCreateLoveLetterRequest, + encodeResponse, + options..., + )) mux := http.NewServeMux() mux.Handle("/api/v1/", http.StripPrefix("/api/v1", m)) @@ -474,6 +480,30 @@ func decodeHTTPGetMatchedLoverRequest(_ context.Context, r *http.Request) (inter } } +// decodeHTTPCreateLoveLetterRequest decode request +func decodeHTTPCreateLoveLetterRequest(_ context.Context, r *http.Request) (interface{}, error) { + if r.Method == "POST" { + var req authorization.CreateLoveLetterRequest + 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.Title == "" { + return nil, utils.NewErrorResponse(utils.TitleRequired) + } + if req.Body == "" { + return nil, utils.NewErrorResponse(utils.BodyRequired) + } + 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 888de5c..f5adf89 100644 --- a/pkg/authorization/users-service.go +++ b/pkg/authorization/users-service.go @@ -983,7 +983,7 @@ func (s *userService) MatchLover(ctx context.Context, request *MatchLoverRequest 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) + cusErr := utils.NewErrorResponse(utils.MatchCodeIsNotExactly) return cusErr } if matchData.Code == "" { @@ -1073,7 +1073,7 @@ func (s *userService) UnMatchedLover(ctx context.Context) error { cusErr := utils.NewErrorResponse(utils.InternalServerError) return cusErr } - s.logger.Debug("Successfully unmatch lover") + s.logger.Debug("Successfully unmatched lover") return nil } @@ -1131,3 +1131,51 @@ func (s *userService) GetMatchLover(ctx context.Context) (interface{}, error) { } return response, nil } + +// CreateLoveLetter create love letter +func (s *userService) CreateLoveLetter(ctx context.Context, request *CreateLoveLetterRequest) 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 + } + // Get match lover + matchID := "" + matchLove, err := s.repo.GetMatchLoveDataByUserID(ctx, user.ID) + if err != nil { + s.logger.Error("User does not match with someone", "error", err) + } + matchID = matchLove.MatchID + // Create love letter + loveLetter := &database.LoveLetter{ + UserID: user.ID, + MatchID: matchID, + Title: request.Title, + Body: request.Body, + } + err = s.repo.CreateLoveLetter(ctx, loveLetter) + if err != nil { + s.logger.Error("Cannot create love letter", "error", err) + if strings.Contains(err.Error(), "value too long for type character varying") { + cusErr := utils.NewErrorResponse(utils.ValueTooLong) + return cusErr + } + cusErr := utils.NewErrorResponse(utils.InternalServerError) + return cusErr + } + s.logger.Debug("Successfully create love letter") + return nil +}