Skip to content

Commit

Permalink
chore: fix export memos
Browse files Browse the repository at this point in the history
  • Loading branch information
boojack committed Feb 4, 2024
1 parent b8a9783 commit 1e07b70
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 328 deletions.
73 changes: 0 additions & 73 deletions api/v2/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,6 @@ const (
usernameContextKey ContextKey = iota
)

// Used to set modified context of ServerStream.
type WrappedStream struct {
ctx context.Context
stream grpc.ServerStream
}

func (w *WrappedStream) RecvMsg(m any) error {
return w.stream.RecvMsg(m)
}

func (w *WrappedStream) SendMsg(m any) error {
return w.stream.SendMsg(m)
}

func (w *WrappedStream) SendHeader(md metadata.MD) error {
return w.stream.SendHeader(md)
}

func (w *WrappedStream) SetHeader(md metadata.MD) error {
return w.stream.SetHeader(md)
}

func (w *WrappedStream) SetTrailer(md metadata.MD) {
w.stream.SetTrailer(md)
}

func (w *WrappedStream) Context() context.Context {
return w.ctx
}

func newWrappedStream(ctx context.Context, stream grpc.ServerStream) grpc.ServerStream {
return &WrappedStream{ctx, stream}
}

// GRPCAuthInterceptor is the auth interceptor for gRPC server.
type GRPCAuthInterceptor struct {
Store *store.Store
Expand Down Expand Up @@ -114,45 +80,6 @@ func (in *GRPCAuthInterceptor) AuthenticationInterceptor(ctx context.Context, re
return handler(childCtx, request)
}

func (in *GRPCAuthInterceptor) StreamAuthenticationInterceptor(srv any, stream grpc.ServerStream, serverInfo *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
md, ok := metadata.FromIncomingContext(stream.Context())
if !ok {
return status.Errorf(codes.Unauthenticated, "failed to parse metadata from incoming context")
}
accessToken, err := getTokenFromMetadata(md)
if err != nil {
return status.Errorf(codes.Unauthenticated, err.Error())
}

username, err := in.authenticate(stream.Context(), accessToken)
if err != nil {
if isUnauthorizeAllowedMethod(serverInfo.FullMethod) {
return handler(stream.Context(), stream)
}
return err
}
user, err := in.Store.GetUser(stream.Context(), &store.FindUser{
Username: &username,
})
if err != nil {
return errors.Wrap(err, "failed to get user")
}
if user == nil {
return errors.Errorf("user %q not exists", username)
}
if user.RowStatus == store.Archived {
return errors.Errorf("user %q is archived", username)
}
if isOnlyForAdminAllowedMethod(serverInfo.FullMethod) && user.Role != store.RoleHost && user.Role != store.RoleAdmin {
return errors.Errorf("user %q is not admin", username)
}

// Stores userID into context.
childCtx := context.WithValue(stream.Context(), usernameContextKey, username)

return handler(srv, newWrappedStream(childCtx, stream))
}

func (in *GRPCAuthInterceptor) authenticate(ctx context.Context, accessToken string) (string, error) {
if accessToken == "" {
return "", status.Errorf(codes.Unauthenticated, "access token not found")
Expand Down
12 changes: 3 additions & 9 deletions api/v2/apidocs.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -481,15 +481,9 @@ paths:
operationId: MemoService_ExportMemos
responses:
"200":
description: A successful response.(streaming responses)
description: A successful response.
schema:
type: object
properties:
result:
$ref: '#/definitions/v2ExportMemosResponse'
error:
$ref: '#/definitions/googlerpcStatus'
title: Stream result of v2ExportMemosResponse
$ref: '#/definitions/v2ExportMemosResponse'
default:
description: An unexpected error response.
schema:
Expand Down Expand Up @@ -1520,7 +1514,7 @@ definitions:
v2ExportMemosResponse:
type: object
properties:
file:
content:
type: string
format: byte
v2GetActivityResponse:
Expand Down
36 changes: 10 additions & 26 deletions api/v2/memo_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,59 +508,43 @@ func (s *APIV2Service) GetUserMemosStats(ctx context.Context, request *apiv2pb.G
return response, nil
}

func (s *APIV2Service) ExportMemos(request *apiv2pb.ExportMemosRequest, srv apiv2pb.MemoService_ExportMemosServer) error {
ctx := srv.Context()
fmt.Printf("%+v\n", ctx)
func (s *APIV2Service) ExportMemos(ctx context.Context, request *apiv2pb.ExportMemosRequest) (*apiv2pb.ExportMemosResponse, error) {
memoFind, err := s.buildFindMemosWithFilter(ctx, request.Filter, true)
if err != nil {
return err
return nil, status.Errorf(codes.Internal, "failed to build find memos with filter")
}

memos, err := s.Store.ListMemos(ctx, memoFind)
if err != nil {
return err
return nil, status.Errorf(codes.Internal, "failed to list memos")
}

buf := new(bytes.Buffer)
writer := zip.NewWriter(buf)

for _, memo := range memos {
memoMessage, err := s.convertMemoFromStore(ctx, memo)
log.Info(memoMessage.Content)
if err != nil {
return errors.Wrap(err, "failed to convert memo")
return nil, errors.Wrap(err, "failed to convert memo")
}
file, err := writer.Create(time.Unix(memo.CreatedTs, 0).Format(time.RFC3339) + ".md")
if err != nil {
return status.Errorf(codes.Internal, "Failed to create memo file")
return nil, status.Errorf(codes.Internal, "Failed to create memo file")
}
_, err = file.Write([]byte(memoMessage.Content))
if err != nil {
return status.Errorf(codes.Internal, "Failed to write to memo file")
return nil, status.Errorf(codes.Internal, "Failed to write to memo file")
}
}

err = writer.Close()
if err != nil {
return status.Errorf(codes.Internal, "Failed to close zip file writer")
}

exportChunk := &apiv2pb.ExportMemosResponse{}
sizeOfFile := len(buf.Bytes())
for currentByte := 0; currentByte < sizeOfFile; currentByte += ChunkSize {
if currentByte+ChunkSize > sizeOfFile {
exportChunk.File = buf.Bytes()[currentByte:sizeOfFile]
} else {
exportChunk.File = buf.Bytes()[currentByte : currentByte+ChunkSize]
}

err := srv.Send(exportChunk)
if err != nil {
return status.Error(codes.Internal, "Unable to stream ExportMemosResponse chunk")
}
return nil, status.Errorf(codes.Internal, "Failed to close zip file writer")
}

return nil
return &apiv2pb.ExportMemosResponse{
Content: buf.Bytes(),
}, nil
}

func (s *APIV2Service) convertMemoFromStore(ctx context.Context, memo *store.Memo) (*apiv2pb.Memo, error) {
Expand Down
3 changes: 0 additions & 3 deletions api/v2/v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ func NewAPIV2Service(secret string, profile *profile.Profile, store *store.Store
grpc.ChainUnaryInterceptor(
authProvider.AuthenticationInterceptor,
),
grpc.ChainStreamInterceptor(
authProvider.StreamAuthenticationInterceptor,
),
)
apiv2Service := &APIV2Service{
Secret: secret,
Expand Down
4 changes: 2 additions & 2 deletions proto/api/v2/memo_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ service MemoService {
option (google.api.method_signature) = "id";
}
// ExportMemos exports memos.
rpc ExportMemos(ExportMemosRequest) returns (stream ExportMemosResponse) {
rpc ExportMemos(ExportMemosRequest) returns (ExportMemosResponse) {
option (google.api.http) = {post: "/api/v2/memos:export"};
}
// GetUserMemosStats gets stats of memos for a user.
Expand Down Expand Up @@ -281,5 +281,5 @@ message ExportMemosRequest {
}

message ExportMemosResponse {
bytes file = 1;
bytes content = 1;
}
4 changes: 2 additions & 2 deletions proto/gen/api/v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1468,7 +1468,7 @@ Used internally for obfuscating the page token.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| file | [bytes](#bytes) | | |
| content | [bytes](#bytes) | | |



Expand Down Expand Up @@ -1857,7 +1857,7 @@ Used internally for obfuscating the page token.
| ListMemoRelations | [ListMemoRelationsRequest](#memos-api-v2-ListMemoRelationsRequest) | [ListMemoRelationsResponse](#memos-api-v2-ListMemoRelationsResponse) | ListMemoRelations lists relations for a memo. |
| CreateMemoComment | [CreateMemoCommentRequest](#memos-api-v2-CreateMemoCommentRequest) | [CreateMemoCommentResponse](#memos-api-v2-CreateMemoCommentResponse) | CreateMemoComment creates a comment for a memo. |
| ListMemoComments | [ListMemoCommentsRequest](#memos-api-v2-ListMemoCommentsRequest) | [ListMemoCommentsResponse](#memos-api-v2-ListMemoCommentsResponse) | ListMemoComments lists comments for a memo. |
| ExportMemos | [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) | [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) stream | ExportMemos exports memos. |
| ExportMemos | [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) | [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) | ExportMemos exports memos. |
| GetUserMemosStats | [GetUserMemosStatsRequest](#memos-api-v2-GetUserMemosStatsRequest) | [GetUserMemosStatsResponse](#memos-api-v2-GetUserMemosStatsResponse) | GetUserMemosStats gets stats of memos for a user. |


Expand Down
Loading

0 comments on commit 1e07b70

Please sign in to comment.