From 675b01e162f3b34d8925c89eeac0ad90d8005c62 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:14:04 +0700 Subject: [PATCH 1/4] fix: log --- internal/baan/baan.handler.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/baan/baan.handler.go b/internal/baan/baan.handler.go index 9c9fab0..04ec941 100644 --- a/internal/baan/baan.handler.go +++ b/internal/baan/baan.handler.go @@ -33,6 +33,7 @@ func (h *handlerImpl) FindAllBaan(c router.Context) { req := &dto.FindAllBaanRequest{} res, appErr := h.svc.FindAllBaan(req) if appErr != nil { + h.log.Named("FindAllBaan").Error("FindAllBaan: ", zap.Error(appErr)) c.ResponseError(appErr) return } @@ -51,13 +52,14 @@ func (h *handlerImpl) FindOneBaan(c router.Context) { } if errorList := h.validate.Validate(req); errorList != nil { - h.log.Named("baan hdr").Error("validation error", zap.Strings("errorList", errorList)) + h.log.Named("FineOneBaan").Error("Validate: ", zap.Strings("errorList", errorList)) c.BadRequestError(strings.Join(errorList, ", ")) return } res, appErr := h.svc.FindOneBaan(req) if appErr != nil { + h.log.Named("FindOneBaan").Error("FindOneBaan: ", zap.Error(appErr)) c.ResponseError(appErr) return } From 6cfbd460f11176aca1466d5dbbabb12a65c6a63d Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:15:01 +0700 Subject: [PATCH 2/4] fix: log --- internal/baan/baan.service.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/baan/baan.service.go b/internal/baan/baan.service.go index 7aee13e..9a0e361 100644 --- a/internal/baan/baan.service.go +++ b/internal/baan/baan.service.go @@ -35,6 +35,7 @@ func (s *serviceImpl) FindAllBaan(req *dto.FindAllBaanRequest) (*dto.FindAllBaan res, err := s.client.FindAllBaan(ctx, &baanProto.FindAllBaanRequest{}) if err != nil { + s.log.Named("FindAllBaan").Error("FindAllBaan: ", zap.Error(err)) st, ok := status.FromError(err) if !ok { return nil, apperror.InternalServer @@ -62,6 +63,7 @@ func (s *serviceImpl) FindOneBaan(req *dto.FindOneBaanRequest) (*dto.FindOneBaan Id: req.Id, }) if err != nil { + s.log.Named("FindOneBaan").Error("FindOneBaan: ", zap.Error(err)) st, ok := status.FromError(err) if !ok { return nil, apperror.InternalServer From 38630f8f2b079f3aa0136c89607faf3b8ecb8c49 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:15:21 +0700 Subject: [PATCH 3/4] feat: group service --- internal/dto/group.dto.go | 77 +++++++ internal/group/group.service.go | 238 ++++++++++++++++++++++ internal/group/test/group.service_test.go | 1 + 3 files changed, 316 insertions(+) create mode 100644 internal/dto/group.dto.go create mode 100644 internal/group/group.service.go create mode 100644 internal/group/test/group.service_test.go diff --git a/internal/dto/group.dto.go b/internal/dto/group.dto.go new file mode 100644 index 0000000..e0feb08 --- /dev/null +++ b/internal/dto/group.dto.go @@ -0,0 +1,77 @@ +package dto + +type Group struct { + Id string `json:"id"` + LeaderID string `json:"leader_id"` + Token string `json:"token"` + Members []*UserInfo `json:"members"` +} + +type UserInfo struct { + Id string `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + ImageUrl string `json:"image_url"` +} + +type FindOneGroupRequest struct { + UserId string `json:"user_id"` +} + +type FindOneGroupResponse struct { + Group *Group `json:"group"` +} + +type FindByTokenGroupRequest struct { + Token string `json:"token"` +} + +type FindByTokenGroupResponse struct { + Id string `json:"id"` + Token string `json:"token"` + Leader *UserInfo `json:"leader"` +} + +type UpdateGroupRequest struct { + Group *Group `json:"group"` + LeaderId string `json:"leader_id"` +} + +type UpdateGroupResponse struct { + Group *Group `json:"group"` +} + +type JoinGroupRequest struct { + Token string `json:"token"` + UserId string `json:"user_id"` +} + +type JoinGroupResponse struct { + Group *Group `json:"group"` +} + +type DeleteMemberGroupRequest struct { + UserId string `json:"user_id"` + LeaderId string `json:"leader_id"` +} + +type DeleteMemberGroupResponse struct { + Group *Group `json:"group"` +} + +type LeaveGroupRequest struct { + UserId string `json:"user_id"` +} + +type LeaveGroupResponse struct { + Group *Group `json:"group"` +} + +type SelectBaanRequest struct { + UserId string `json:"user_id"` + Baans []string `json:"baans"` +} + +type SelectBaanResponse struct { + Success bool `json:"success"` +} diff --git a/internal/group/group.service.go b/internal/group/group.service.go new file mode 100644 index 0000000..0c1ac83 --- /dev/null +++ b/internal/group/group.service.go @@ -0,0 +1,238 @@ +package group + +import ( + "context" + "time" + + "github.com/isd-sgcu/rpkm67-gateway/apperror" + "github.com/isd-sgcu/rpkm67-gateway/internal/dto" + groupProto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/backend/group/v1" + "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type Service interface { + FindOne(req *dto.FindOneGroupRequest) (*dto.FindOneGroupResponse, *apperror.AppError) + FindByToken(req *dto.FindByTokenGroupRequest) (*dto.FindByTokenGroupResponse, *apperror.AppError) + Update(req *dto.UpdateGroupRequest) (*dto.UpdateGroupResponse, *apperror.AppError) + Join(req *dto.JoinGroupRequest) (*dto.JoinGroupResponse, *apperror.AppError) + DeleteMember(req *dto.DeleteMemberGroupRequest) (*dto.DeleteMemberGroupResponse, *apperror.AppError) + Leave(req *dto.LeaveGroupRequest) (*dto.LeaveGroupResponse, *apperror.AppError) + SelectBaan(req *dto.SelectBaanRequest) (*dto.SelectBaanResponse, *apperror.AppError) +} + +type serviceImpl struct { + client groupProto.GroupServiceClient + log *zap.Logger +} + +func NewService(client groupProto.GroupServiceClient, log *zap.Logger) Service { + return &serviceImpl{ + client: client, + log: log, + } +} + +func (s *serviceImpl) DeleteMember(req *dto.DeleteMemberGroupRequest) (*dto.DeleteMemberGroupResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.DeleteMember(ctx, &groupProto.DeleteMemberGroupRequest{ + UserId: req.UserId, + LeaderId: req.LeaderId, + }) + if err != nil { + s.log.Named("DeleteMember").Error("DeleteMember: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.DeleteMemberGroupResponse{ + Group: GroupProtoToDto(res.Group), + }, nil +} + +func (s *serviceImpl) FindByToken(req *dto.FindByTokenGroupRequest) (*dto.FindByTokenGroupResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.FindByToken(ctx, &groupProto.FindByTokenGroupRequest{ + Token: req.Token, + }) + if err != nil { + s.log.Named("FindByToken").Error("FindByToken: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("Invalid argument") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.FindByTokenGroupResponse{ + Id: res.Id, + Token: res.Token, + Leader: UserInfoProtoToDto(res.Leader), + }, nil +} + +func (s *serviceImpl) FindOne(req *dto.FindOneGroupRequest) (*dto.FindOneGroupResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.FindOne(ctx, &groupProto.FindOneGroupRequest{ + UserId: req.UserId, + }) + if err != nil { + s.log.Named("FindOne").Error("FindOne: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("Invalid argument") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.FindOneGroupResponse{ + Group: GroupProtoToDto(res.Group), + }, nil +} + +func (s *serviceImpl) Join(req *dto.JoinGroupRequest) (*dto.JoinGroupResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.Join(ctx, &groupProto.JoinGroupRequest{ + Token: req.Token, + UserId: req.UserId, + }) + + if err != nil { + s.log.Named("Join").Error("Join: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("Invalid argument") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.JoinGroupResponse{ + Group: GroupProtoToDto(res.Group), + }, nil +} + +func (s *serviceImpl) Leave(req *dto.LeaveGroupRequest) (*dto.LeaveGroupResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.Leave(ctx, &groupProto.LeaveGroupRequest{ + UserId: req.UserId, + }) + if err != nil { + s.log.Named("Leave").Error("Leave: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("Invalid argument") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.LeaveGroupResponse{ + Group: GroupProtoToDto(res.Group), + }, nil +} + +func (s *serviceImpl) SelectBaan(req *dto.SelectBaanRequest) (*dto.SelectBaanResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.SelectBaan(ctx, &groupProto.SelectBaanRequest{ + UserId: req.UserId, + Baans: req.Baans, + }) + if err != nil { + s.log.Named("SelectBaan").Error("SelectBaan: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("Invalid argument") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.SelectBaanResponse{ + Success: res.Success, + }, nil +} + +func (s *serviceImpl) Update(req *dto.UpdateGroupRequest) (*dto.UpdateGroupResponse, *apperror.AppError) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + res, err := s.client.Update(ctx, &groupProto.UpdateGroupRequest{ + Group: GroupDtoToProto(req.Group), + LeaderId: req.LeaderId, + }) + if err != nil { + s.log.Named("Update").Error("Update: ", zap.Error(err)) + st, ok := status.FromError(err) + if !ok { + return nil, apperror.InternalServer + } + switch st.Code() { + case codes.InvalidArgument: + return nil, apperror.BadRequestError("Invalid argument") + case codes.Internal: + return nil, apperror.InternalServerError(err.Error()) + default: + return nil, apperror.ServiceUnavailable + } + } + + return &dto.UpdateGroupResponse{ + Group: GroupProtoToDto(res.Group), + }, nil +} diff --git a/internal/group/test/group.service_test.go b/internal/group/test/group.service_test.go new file mode 100644 index 0000000..56e5404 --- /dev/null +++ b/internal/group/test/group.service_test.go @@ -0,0 +1 @@ +package test From 88520493719fdb98315665c7830aefdf2f46e5a2 Mon Sep 17 00:00:00 2001 From: Gear <84141000+macgeargear@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:15:29 +0700 Subject: [PATCH 4/4] feat: group handler --- internal/group/group.handler.go | 248 ++++++++++++++++++++++ internal/group/group.utils.go | 63 ++++++ internal/group/test/group.handler_test.go | 1 + 3 files changed, 312 insertions(+) create mode 100644 internal/group/group.handler.go create mode 100644 internal/group/group.utils.go create mode 100644 internal/group/test/group.handler_test.go diff --git a/internal/group/group.handler.go b/internal/group/group.handler.go new file mode 100644 index 0000000..c2e622d --- /dev/null +++ b/internal/group/group.handler.go @@ -0,0 +1,248 @@ +package group + +import ( + "net/http" + "strings" + + "github.com/isd-sgcu/rpkm67-gateway/internal/dto" + "github.com/isd-sgcu/rpkm67-gateway/internal/router" + "github.com/isd-sgcu/rpkm67-gateway/internal/validator" + "go.uber.org/zap" +) + +type Handler interface { + FindOne(c router.Context) + FindByToken(c router.Context) + Update(c router.Context) + Join(c router.Context) + DeleteMember(c router.Context) + Leave(c router.Context) + SelectBaan(c router.Context) +} + +func NewHandler(svc Service, validate validator.DtoValidator, log *zap.Logger) Handler { + return &handlerImpl{ + svc: svc, + validate: validate, + log: log, + } +} + +type handlerImpl struct { + svc Service + validate validator.DtoValidator + log *zap.Logger +} + +func (h *handlerImpl) DeleteMember(c router.Context) { + body := &dto.DeleteMemberGroupRequest{} + if err := c.Bind(body); err != nil { + h.log.Named("DeleteMember").Error("Bind: failed to bind request body", zap.Error(err)) + c.BadRequestError(err.Error()) + return + } + + if errorList := h.validate.Validate(body); errorList != nil { + h.log.Named("DeleteMember").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + req := &dto.DeleteMemberGroupRequest{ + UserId: body.UserId, + LeaderId: body.LeaderId, + } + + res, appErr := h.svc.DeleteMember(req) + if appErr != nil { + h.log.Named("DeleteMember").Error("DeleteMember: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.DeleteMemberGroupResponse{ + Group: res.Group, + }) +} + +func (h *handlerImpl) FindByToken(c router.Context) { + body := &dto.FindByTokenGroupRequest{} + if err := c.Bind(body); err != nil { + h.log.Named("FindByToken").Error("Bind: failed to bind request body", zap.Error(err)) + c.BadRequestError(err.Error()) + return + } + + if errorList := h.validate.Validate(body); errorList != nil { + h.log.Named("FindByToken").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + req := &dto.FindByTokenGroupRequest{ + Token: body.Token, + } + + res, appErr := h.svc.FindByToken(req) + if appErr != nil { + h.log.Named("FindByToken").Error("FindByToken: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.FindByTokenGroupResponse{ + Id: res.Id, + Token: res.Token, + Leader: res.Leader, + }) +} + +func (h *handlerImpl) FindOne(c router.Context) { + userId := c.Param("id") + if userId == "" { + c.BadRequestError("url parameter 'user_id' not found") + } + + req := &dto.FindOneGroupRequest{ + UserId: userId, + } + + if errorList := h.validate.Validate(req); errorList != nil { + h.log.Named("FindOne").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + res, appErr := h.svc.FindOne(req) + if appErr != nil { + h.log.Named("FindOne").Error("FindOne: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.FindOneGroupResponse{ + Group: res.Group, + }) +} + +func (h *handlerImpl) Join(c router.Context) { + body := &dto.JoinGroupRequest{} + if err := c.Bind(body); err != nil { + h.log.Named("Join").Error("Bind: failed to bind request body", zap.Error(err)) + c.BadRequestError(err.Error()) + return + } + + if errorList := h.validate.Validate(body); errorList != nil { + h.log.Named("Join").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + req := &dto.JoinGroupRequest{ + Token: body.Token, + UserId: body.UserId, + } + + res, appErr := h.svc.Join(req) + if appErr != nil { + h.log.Named("Join").Error("Join: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.JoinGroupResponse{ + Group: res.Group, + }) +} + +func (h *handlerImpl) Leave(c router.Context) { + body := &dto.LeaveGroupRequest{} + if err := c.Bind(body); err != nil { + h.log.Named("Leave").Error("Bind: failed to bind request body", zap.Error(err)) + c.BadRequestError(err.Error()) + return + } + + if errorList := h.validate.Validate(body); errorList != nil { + h.log.Named("Leave").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + req := &dto.LeaveGroupRequest{ + UserId: body.UserId, + } + + res, appErr := h.svc.Leave(req) + if appErr != nil { + h.log.Named("Leave").Error("Leave: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.LeaveGroupResponse{ + Group: res.Group, + }) +} + +func (h *handlerImpl) SelectBaan(c router.Context) { + body := &dto.SelectBaanRequest{} + if err := c.Bind(body); err != nil { + h.log.Named("SelectBaan").Error("Bind: failed to bind request body", zap.Error(err)) + c.BadRequestError(err.Error()) + return + } + + if errorList := h.validate.Validate(body); errorList != nil { + h.log.Named("SelectBaan").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + req := &dto.SelectBaanRequest{ + UserId: body.UserId, + Baans: body.Baans, + } + + res, appErr := h.svc.SelectBaan(req) + if appErr != nil { + h.log.Named("SelectBaan").Error("SelectBaan: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.SelectBaanResponse{ + Success: res.Success, + }) +} + +func (h *handlerImpl) Update(c router.Context) { + body := &dto.UpdateGroupRequest{} + if err := c.Bind(body); err != nil { + h.log.Named("Update").Error("Bind: failed to bind request body", zap.Error(err)) + c.BadRequestError(err.Error()) + return + } + + if errorList := h.validate.Validate(body); errorList != nil { + h.log.Named("Update").Error("Validate: ", zap.Strings("errorList", errorList)) + c.BadRequestError(strings.Join(errorList, ", ")) + return + } + + req := &dto.UpdateGroupRequest{ + Group: body.Group, + } + + res, appErr := h.svc.Update(req) + if appErr != nil { + h.log.Named("Update").Error("Update: ", zap.Error(appErr)) + c.ResponseError(appErr) + return + } + + c.JSON(http.StatusOK, &dto.UpdateGroupResponse{ + Group: res.Group, + }) +} diff --git a/internal/group/group.utils.go b/internal/group/group.utils.go new file mode 100644 index 0000000..684452b --- /dev/null +++ b/internal/group/group.utils.go @@ -0,0 +1,63 @@ +package group + +import ( + "github.com/isd-sgcu/rpkm67-gateway/internal/dto" + groupProto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/backend/group/v1" +) + +func GroupProtoToDto(group *groupProto.Group) *dto.Group { + return &dto.Group{ + Id: group.Id, + LeaderID: group.LeaderID, + Token: group.Token, + Members: convertMembers(group.Members), + } +} + +func GroupDtoToProto(group *dto.Group) *groupProto.Group { + var members []*groupProto.UserInfo + for _, member := range group.Members { + members = append(members, &groupProto.UserInfo{ + Id: member.Id, + Firstname: member.FirstName, + Lastname: member.LastName, + ImageUrl: member.ImageUrl, + }) + } + return &groupProto.Group{ + Id: group.Id, + LeaderID: group.LeaderID, + Token: group.Token, + Members: members, + } +} + +func UserInfoProtoToDto(userInfo *groupProto.UserInfo) *dto.UserInfo { + return &dto.UserInfo{ + Id: userInfo.Id, + FirstName: userInfo.Firstname, + LastName: userInfo.Lastname, + ImageUrl: userInfo.ImageUrl, + } +} + +func convertMembers(members []*groupProto.UserInfo) []*dto.UserInfo { + var convertedMembers []*dto.UserInfo + for _, member := range members { + convertedMembers = append(convertedMembers, &dto.UserInfo{ + Id: member.Id, + FirstName: member.Firstname, + LastName: member.Lastname, + ImageUrl: member.ImageUrl, + }) + } + return convertedMembers +} + +func GroupProtoToDtoList(groups []*groupProto.Group) []*dto.Group { + var result []*dto.Group + for _, group := range groups { + result = append(result, GroupProtoToDto(group)) + } + return result +} diff --git a/internal/group/test/group.handler_test.go b/internal/group/test/group.handler_test.go new file mode 100644 index 0000000..56e5404 --- /dev/null +++ b/internal/group/test/group.handler_test.go @@ -0,0 +1 @@ +package test