github.com/yandex/pandora@v0.5.32/examples/grpc/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log/slog"
     7  	"strconv"
     8  	"sync"
     9  
    10  	"github.com/yandex/pandora/lib/str"
    11  	"google.golang.org/grpc/codes"
    12  	"google.golang.org/grpc/status"
    13  )
    14  
    15  const (
    16  	userCount         = 10
    17  	userMultiplicator = 1000
    18  	itemMultiplicator = 100
    19  )
    20  
    21  func NewServer(logger *slog.Logger, seed int64) *GRPCServer {
    22  	keys := make(map[string]int64, userCount)
    23  	for i := int64(1); i <= userCount; i++ {
    24  		keys[str.RandStringRunes(64, "")] = i
    25  	}
    26  	logger.Info("New server created", slog.Any("keys", keys))
    27  
    28  	return &GRPCServer{Log: logger, keys: keys, stats: newStats(userCount)}
    29  }
    30  
    31  type GRPCServer struct {
    32  	UnimplementedTargetServiceServer
    33  	Log   *slog.Logger
    34  	stats *Stats
    35  	keys  map[string]int64
    36  	mu    sync.RWMutex
    37  }
    38  
    39  var _ TargetServiceServer = (*GRPCServer)(nil)
    40  
    41  func (s *GRPCServer) Hello(ctx context.Context, request *HelloRequest) (*HelloResponse, error) {
    42  	s.stats.IncHello()
    43  	return &HelloResponse{
    44  		Hello: fmt.Sprintf("Hello %s!", request.Name),
    45  	}, nil
    46  }
    47  
    48  func (s *GRPCServer) Auth(ctx context.Context, request *AuthRequest) (*AuthResponse, error) {
    49  	userID, token, err := s.checkLoginPass(request.GetLogin(), request.GetPass())
    50  	if err != nil {
    51  		s.stats.IncAuth400()
    52  		return nil, status.Error(codes.InvalidArgument, "invalid credentials")
    53  	}
    54  	result := &AuthResponse{
    55  		UserId: userID,
    56  		Token:  token,
    57  	}
    58  	s.stats.IncAuth200(userID)
    59  	return result, nil
    60  }
    61  
    62  func (s *GRPCServer) List(ctx context.Context, request *ListRequest) (*ListResponse, error) {
    63  	s.mu.RLock()
    64  	userID := s.keys[request.Token]
    65  	s.mu.RUnlock()
    66  	if userID == 0 {
    67  		s.stats.IncList400()
    68  		return nil, status.Error(codes.InvalidArgument, "invalid token")
    69  	}
    70  	if userID != request.UserId {
    71  		s.stats.IncList400()
    72  		return nil, status.Error(codes.InvalidArgument, "invalid user_id")
    73  	}
    74  
    75  	// Logic
    76  	result := &ListResponse{}
    77  	userID *= userMultiplicator
    78  	result.Result = make([]*ListItem, itemMultiplicator)
    79  	for i := int64(0); i < itemMultiplicator; i++ {
    80  		result.Result[i] = &ListItem{ItemId: userID + i}
    81  	}
    82  	s.stats.IncList200(request.UserId)
    83  	return result, nil
    84  }
    85  
    86  func (s *GRPCServer) Order(ctx context.Context, request *OrderRequest) (*OrderResponse, error) {
    87  	s.mu.RLock()
    88  	userID := s.keys[request.Token]
    89  	s.mu.RUnlock()
    90  	if userID == 0 {
    91  		s.stats.IncOrder400()
    92  		return nil, status.Error(codes.InvalidArgument, "invalid token")
    93  	}
    94  	if userID != request.UserId {
    95  		s.stats.IncOrder400()
    96  		return nil, status.Error(codes.InvalidArgument, "invalid user_id")
    97  	}
    98  
    99  	// Logic
   100  	ranger := userID * userMultiplicator
   101  	if request.ItemId < ranger || request.ItemId >= ranger+itemMultiplicator {
   102  		s.stats.IncOrder400()
   103  		return nil, status.Error(codes.InvalidArgument, "invalid item_id")
   104  	}
   105  
   106  	result := &OrderResponse{}
   107  	result.OrderId = request.ItemId + 12345
   108  	s.stats.IncOrder200(userID)
   109  	return result, nil
   110  }
   111  
   112  func (s *GRPCServer) Stats(ctx context.Context, _ *StatsRequest) (*StatsResponse, error) {
   113  	result := &StatsResponse{
   114  		Hello: int64(s.stats.hello.Load()),
   115  		Auth: &StatisticBodyResponse{
   116  			Code200: s.stats.auth200,
   117  			Code400: s.stats.auth400.Load(),
   118  			Code500: s.stats.auth500.Load(),
   119  		},
   120  		List: &StatisticBodyResponse{
   121  			Code200: s.stats.list200,
   122  			Code400: s.stats.list400.Load(),
   123  			Code500: s.stats.list500.Load(),
   124  		},
   125  		Order: &StatisticBodyResponse{
   126  			Code200: s.stats.order200,
   127  			Code400: s.stats.order400.Load(),
   128  			Code500: s.stats.order500.Load(),
   129  		},
   130  	}
   131  	return result, nil
   132  }
   133  
   134  func (s *GRPCServer) Reset(ctx context.Context, request *ResetRequest) (*ResetResponse, error) {
   135  	s.stats.Reset()
   136  
   137  	result := &ResetResponse{
   138  		Auth: &StatisticBodyResponse{
   139  			Code200: s.stats.auth200,
   140  			Code400: s.stats.auth400.Load(),
   141  			Code500: s.stats.auth500.Load(),
   142  		},
   143  		List: &StatisticBodyResponse{
   144  			Code200: s.stats.list200,
   145  			Code400: s.stats.list400.Load(),
   146  			Code500: s.stats.list500.Load(),
   147  		},
   148  		Order: &StatisticBodyResponse{
   149  			Code200: s.stats.order200,
   150  			Code400: s.stats.order400.Load(),
   151  			Code500: s.stats.order500.Load(),
   152  		},
   153  	}
   154  	return result, nil
   155  }
   156  
   157  func (s *GRPCServer) checkLoginPass(login string, pass string) (int64, string, error) {
   158  	userID, err := strconv.ParseInt(login, 10, 64)
   159  	if err != nil {
   160  		return 0, "", fmt.Errorf("invalid login %s", login)
   161  	}
   162  	if login != pass {
   163  		return 0, "", fmt.Errorf("invalid login %s or pass %s", login, pass)
   164  	}
   165  	token := ""
   166  	s.mu.RLock()
   167  	for k, v := range s.keys {
   168  		if v == userID {
   169  			token = k
   170  			break
   171  		}
   172  	}
   173  	s.mu.RUnlock()
   174  	if token == "" {
   175  		return 0, "", fmt.Errorf("invalid login %s and pass %s", login, pass)
   176  	}
   177  
   178  	return userID, token, nil
   179  }