slash-e/server/route/api/v1/memo_service.go
2024-03-01 00:08:05 +08:00

197 lines
6.0 KiB
Go

package v1
import (
"context"
"strings"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
apiv1pb "github.com/yourselfhosted/slash/proto/gen/api/v1"
storepb "github.com/yourselfhosted/slash/proto/gen/store"
"github.com/yourselfhosted/slash/store"
)
func (s *APIV2Service) ListMemos(ctx context.Context, _ *apiv1pb.ListMemosRequest) (*apiv1pb.ListMemosResponse, error) {
find := &store.FindMemo{}
memos, err := s.Store.ListMemos(ctx, find)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to fetch memo list, err: %v", err)
}
composedMemos := []*apiv1pb.Memo{}
for _, memo := range memos {
composedMemo, err := s.convertMemoFromStorepb(ctx, memo)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert memo, err: %v", err)
}
composedMemos = append(composedMemos, composedMemo)
}
response := &apiv1pb.ListMemosResponse{
Memos: composedMemos,
}
return response, nil
}
func (s *APIV2Service) GetMemo(ctx context.Context, request *apiv1pb.GetMemoRequest) (*apiv1pb.GetMemoResponse, error) {
memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
ID: &request.Id,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get memo by ID: %v", err)
}
if memo == nil {
return nil, status.Errorf(codes.NotFound, "memo not found")
}
composedMemo, err := s.convertMemoFromStorepb(ctx, memo)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert memo, err: %v", err)
}
response := &apiv1pb.GetMemoResponse{
Memo: composedMemo,
}
return response, nil
}
func (s *APIV2Service) CreateMemo(ctx context.Context, request *apiv1pb.CreateMemoRequest) (*apiv1pb.CreateMemoResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
}
memoCreate := &storepb.Memo{
CreatorId: user.ID,
Name: request.Memo.Name,
Title: request.Memo.Title,
Content: request.Memo.Content,
Tags: request.Memo.Tags,
Visibility: storepb.Visibility(request.Memo.Visibility),
}
memo, err := s.Store.CreateMemo(ctx, memoCreate)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create memo, err: %v", err)
}
composedMemo, err := s.convertMemoFromStorepb(ctx, memo)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert memo, err: %v", err)
}
response := &apiv1pb.CreateMemoResponse{
Memo: composedMemo,
}
return response, nil
}
func (s *APIV2Service) UpdateMemo(ctx context.Context, request *apiv1pb.UpdateMemoRequest) (*apiv1pb.UpdateMemoResponse, error) {
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "updateMask is required")
}
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
}
currentUser, err := s.Store.GetUser(ctx, &store.FindUser{
ID: &user.ID,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user, err: %v", err)
}
memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
ID: &request.Memo.Id,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get memo by ID: %v", err)
}
if memo == nil {
return nil, status.Errorf(codes.NotFound, "memo not found")
}
if memo.CreatorId != user.ID && currentUser.Role != store.RoleAdmin {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
update := &store.UpdateMemo{
ID: memo.Id,
}
for _, path := range request.UpdateMask.Paths {
switch path {
case "name":
update.Name = &request.Memo.Name
case "title":
update.Title = &request.Memo.Title
case "content":
update.Content = &request.Memo.Content
case "tags":
tag := strings.Join(request.Memo.Tags, " ")
update.Tag = &tag
case "visibility":
visibility := store.Visibility(request.Memo.Visibility.String())
update.Visibility = &visibility
}
}
memo, err = s.Store.UpdateMemo(ctx, update)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update memo, err: %v", err)
}
composedMemo, err := s.convertMemoFromStorepb(ctx, memo)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to convert memo, err: %v", err)
}
response := &apiv1pb.UpdateMemoResponse{
Memo: composedMemo,
}
return response, nil
}
func (s *APIV2Service) DeleteMemo(ctx context.Context, request *apiv1pb.DeleteMemoRequest) (*apiv1pb.DeleteMemoResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
}
currentUser, err := s.Store.GetUser(ctx, &store.FindUser{
ID: &user.ID,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user, err: %v", err)
}
memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
ID: &request.Id,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get memo by ID: %v", err)
}
if memo == nil {
return nil, status.Errorf(codes.NotFound, "memo not found")
}
if memo.CreatorId != user.ID && currentUser.Role != store.RoleAdmin {
return nil, status.Errorf(codes.PermissionDenied, "Permission denied")
}
err = s.Store.DeleteMemo(ctx, &store.DeleteMemo{
ID: memo.Id,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete memo, err: %v", err)
}
response := &apiv1pb.DeleteMemoResponse{}
return response, nil
}
func (*APIV2Service) convertMemoFromStorepb(_ context.Context, memo *storepb.Memo) (*apiv1pb.Memo, error) {
return &apiv1pb.Memo{
Id: memo.Id,
CreatedTime: timestamppb.New(time.Unix(memo.CreatedTs, 0)),
UpdatedTime: timestamppb.New(time.Unix(memo.UpdatedTs, 0)),
CreatorId: memo.CreatorId,
Name: memo.Name,
Title: memo.Title,
Content: memo.Content,
Tags: memo.Tags,
Visibility: apiv1pb.Visibility(memo.Visibility),
}, nil
}