74 lines
2.2 KiB
Go
74 lines
2.2 KiB
Go
// Package server exposes the scoreboard Store over gRPC and HTTP/JSON.
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
pb "git.mkz.me/mycroft/root-me-api/gen/scoreboard/v1"
|
|
"git.mkz.me/mycroft/root-me-api/internal/scoreboard"
|
|
)
|
|
|
|
// GRPCServer implements pb.ScoreboardServiceServer over a Store.
|
|
type GRPCServer struct {
|
|
pb.UnimplementedScoreboardServiceServer
|
|
Store *scoreboard.Store
|
|
}
|
|
|
|
// NewGRPC returns a gRPC server backed by the given store.
|
|
func NewGRPC(store *scoreboard.Store) *GRPCServer { return &GRPCServer{Store: store} }
|
|
|
|
func (s *GRPCServer) ListScoreboard(_ context.Context, req *pb.ListScoreboardRequest) (*pb.ListScoreboardResponse, error) {
|
|
if !s.Store.Ready() {
|
|
return nil, status.Error(codes.Unavailable, "scoreboard not loaded yet")
|
|
}
|
|
entries, fetchedAt := s.Store.List(int(req.GetOffset()), int(req.GetLimit()))
|
|
resp := &pb.ListScoreboardResponse{
|
|
Total: int32(s.Store.Len()),
|
|
FetchedAt: fetchedAt.Unix(),
|
|
}
|
|
resp.Entries = make([]*pb.Entry, len(entries))
|
|
for i, e := range entries {
|
|
resp.Entries[i] = toProto(e)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (s *GRPCServer) GetByRank(_ context.Context, req *pb.GetByRankRequest) (*pb.Entry, error) {
|
|
if !s.Store.Ready() {
|
|
return nil, status.Error(codes.Unavailable, "scoreboard not loaded yet")
|
|
}
|
|
e, ok := s.Store.ByRank(int(req.GetRank()))
|
|
if !ok {
|
|
return nil, status.Errorf(codes.NotFound, "no entry at rank %d", req.GetRank())
|
|
}
|
|
return toProto(e), nil
|
|
}
|
|
|
|
func (s *GRPCServer) GetByUsername(_ context.Context, req *pb.GetByUsernameRequest) (*pb.Entry, error) {
|
|
if !s.Store.Ready() {
|
|
return nil, status.Error(codes.Unavailable, "scoreboard not loaded yet")
|
|
}
|
|
if req.GetUsername() == "" {
|
|
return nil, status.Error(codes.InvalidArgument, "username is required")
|
|
}
|
|
e, ok := s.Store.ByUsername(req.GetUsername())
|
|
if !ok {
|
|
return nil, status.Errorf(codes.NotFound, "no entry for user %q", req.GetUsername())
|
|
}
|
|
return toProto(e), nil
|
|
}
|
|
|
|
func toProto(e scoreboard.Entry) *pb.Entry {
|
|
return &pb.Entry{
|
|
Rank: int32(e.Rank),
|
|
Username: e.Username,
|
|
ProfilePath: e.ProfilePath,
|
|
Country: e.Country,
|
|
Grade: e.Grade,
|
|
Score: int32(e.Score),
|
|
}
|
|
}
|