Migrate to gRPC rather than REST with swagger
Will also be adding in a RESTful endpoint to the server as well so it can consume both types
This commit is contained in:
parent
b815284199
commit
7ababb79f6
23 changed files with 1110 additions and 1101 deletions
|
@ -2,69 +2,26 @@ package internal
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/google/uuid"
|
||||
"github.com/mdiluz/rove/pkg/accounts"
|
||||
"github.com/mdiluz/rove/pkg/game"
|
||||
"github.com/mdiluz/rove/pkg/rove"
|
||||
"github.com/mdiluz/rove/pkg/version"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Handler describes a function that handles any incoming request and can respond
|
||||
type Handler func(*Server, map[string]string, io.ReadCloser) (interface{}, error)
|
||||
|
||||
// Route defines the information for a single path->function route
|
||||
type Route struct {
|
||||
path string
|
||||
method string
|
||||
handler Handler
|
||||
}
|
||||
|
||||
// Routes is an array of all the Routes
|
||||
var Routes = []Route{
|
||||
{
|
||||
path: "/status",
|
||||
method: http.MethodGet,
|
||||
handler: HandleStatus,
|
||||
},
|
||||
{
|
||||
path: "/register",
|
||||
method: http.MethodPost,
|
||||
handler: HandleRegister,
|
||||
},
|
||||
{
|
||||
path: "/{account}/command",
|
||||
method: http.MethodPost,
|
||||
handler: HandleCommand,
|
||||
},
|
||||
{
|
||||
path: "/{account}/radar",
|
||||
method: http.MethodGet,
|
||||
handler: HandleRadar,
|
||||
},
|
||||
{
|
||||
path: "/{account}/rover",
|
||||
method: http.MethodGet,
|
||||
handler: HandleRover,
|
||||
},
|
||||
}
|
||||
|
||||
// HandleStatus handles the /status request
|
||||
func HandleStatus(s *Server, vars map[string]string, b io.ReadCloser) (interface{}, error) {
|
||||
|
||||
// Simply return the current server status
|
||||
response := rove.StatusResponse{
|
||||
func (s *Server) Status(context.Context, *empty.Empty) (*rove.StatusResponse, error) {
|
||||
response := &rove.StatusResponse{
|
||||
Ready: true,
|
||||
Version: version.Version,
|
||||
Tick: s.tick,
|
||||
Tick: int32(s.tick),
|
||||
}
|
||||
|
||||
// TODO: Verify the accountant is up and ready too
|
||||
|
||||
// If there's a schedule, respond with it
|
||||
if len(s.schedule.Entries()) > 0 {
|
||||
response.NextTick = s.schedule.Entries()[0].Next.Format("15:04:05")
|
||||
|
@ -73,31 +30,15 @@ func HandleStatus(s *Server, vars map[string]string, b io.ReadCloser) (interface
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// HandleRegister handles /register endpoint
|
||||
func HandleRegister(s *Server, vars map[string]string, b io.ReadCloser) (interface{}, error) {
|
||||
var response = rove.RegisterResponse{}
|
||||
|
||||
// Decode the registration info, verify it and register the account
|
||||
var data rove.RegisterData
|
||||
err := json.NewDecoder(b).Decode(&data)
|
||||
if err != nil {
|
||||
log.Printf("Failed to decode json: %s\n", err)
|
||||
return BadRequestError{Error: err.Error()}, nil
|
||||
|
||||
} else if len(data.Name) == 0 {
|
||||
return BadRequestError{Error: "cannot register empty name"}, nil
|
||||
|
||||
func (s *Server) Register(ctx context.Context, req *rove.RegisterRequest) (*empty.Empty, error) {
|
||||
if len(req.Name) == 0 {
|
||||
return nil, fmt.Errorf("empty account name")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
reg := accounts.RegisterInfo{Name: data.Name}
|
||||
if acc, err := s.accountant.Register(ctx, ®, grpc.WaitForReady(true)); err != nil {
|
||||
return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err)
|
||||
|
||||
} else if !acc.Success {
|
||||
return BadRequestError{Error: acc.Error}, nil
|
||||
if _, err := s.accountant.Register(ctx, &accounts.RegisterInfo{Name: req.Name}, grpc.WaitForReady(true)); err != nil {
|
||||
return nil, err
|
||||
|
||||
} else if _, _, err := s.SpawnRoverForAccount(data.Name); err != nil {
|
||||
} else if _, _, err := s.SpawnRoverForAccount(req.Name); err != nil {
|
||||
return nil, fmt.Errorf("failed to spawn rover for account: %s", err)
|
||||
|
||||
} else if err := s.SaveWorld(); err != nil {
|
||||
|
@ -105,66 +46,48 @@ func HandleRegister(s *Server, vars map[string]string, b io.ReadCloser) (interfa
|
|||
|
||||
}
|
||||
|
||||
log.Printf("register response:%+v\n", response)
|
||||
return response, nil
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
// HandleSpawn will spawn the player entity for the associated account
|
||||
func HandleCommand(s *Server, vars map[string]string, b io.ReadCloser) (interface{}, error) {
|
||||
var response = rove.CommandResponse{}
|
||||
func (s *Server) Rover(ctx context.Context, req *rove.RoverRequest) (*rove.RoverResponse, error) {
|
||||
response := &rove.RoverResponse{}
|
||||
if len(req.Account) == 0 {
|
||||
return nil, fmt.Errorf("empty account name")
|
||||
|
||||
id := vars["account"]
|
||||
|
||||
// Decode the commands, verify them and the account, and execute the commands
|
||||
var data rove.CommandData
|
||||
if err := json.NewDecoder(b).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode json: %s\n", err)
|
||||
return BadRequestError{Error: err.Error()}, nil
|
||||
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
key := accounts.DataKey{Account: id, Key: "rover"}
|
||||
if len(id) == 0 {
|
||||
return BadRequestError{Error: "no account ID provided"}, nil
|
||||
|
||||
} else if resp, err := s.accountant.GetValue(ctx, &key); err != nil {
|
||||
} else if resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"}); err != nil {
|
||||
return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err)
|
||||
|
||||
} else if !resp.Success {
|
||||
return BadRequestError{Error: resp.Error}, nil
|
||||
|
||||
} else if id, err := uuid.Parse(resp.Value); err != nil {
|
||||
return nil, fmt.Errorf("account had invalid rover ID: %s", resp.Value)
|
||||
|
||||
} else if err := s.world.Enqueue(id, data.Commands...); err != nil {
|
||||
return BadRequestError{Error: err.Error()}, nil
|
||||
} else if attrib, err := s.world.RoverAttributes(id); err != nil {
|
||||
return nil, fmt.Errorf("error getting rover attributes: %s", err)
|
||||
|
||||
} else {
|
||||
response = &rove.RoverResponse{
|
||||
Name: attrib.Name,
|
||||
Position: &rove.Vector{
|
||||
X: int32(attrib.Pos.X),
|
||||
Y: int32(attrib.Pos.Y),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("command response \taccount:%s\tresponse:%+v\n", id, response)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// HandleRadar handles the radar request
|
||||
func HandleRadar(s *Server, vars map[string]string, b io.ReadCloser) (interface{}, error) {
|
||||
var response = rove.RadarResponse{}
|
||||
func (s *Server) Radar(ctx context.Context, req *rove.RadarRequest) (*rove.RadarResponse, error) {
|
||||
if len(req.Account) == 0 {
|
||||
return nil, fmt.Errorf("empty account name")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
id := vars["account"]
|
||||
key := accounts.DataKey{Account: id, Key: "rover"}
|
||||
if len(id) == 0 {
|
||||
return BadRequestError{Error: "no account ID provided"}, nil
|
||||
response := &rove.RadarResponse{}
|
||||
|
||||
} else if resp, err := s.accountant.GetValue(ctx, &key); err != nil {
|
||||
resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err)
|
||||
}
|
||||
|
||||
} else if !resp.Success {
|
||||
return BadRequestError{Error: resp.Error}, nil
|
||||
|
||||
} else if id, err := uuid.Parse(resp.Value); err != nil {
|
||||
if id, err := uuid.Parse(resp.Value); err != nil {
|
||||
return nil, fmt.Errorf("account had invalid rover ID: %s", resp.Value)
|
||||
|
||||
} else if attrib, err := s.world.RoverAttributes(id); err != nil {
|
||||
|
@ -175,40 +98,38 @@ func HandleRadar(s *Server, vars map[string]string, b io.ReadCloser) (interface{
|
|||
|
||||
} else {
|
||||
response.Tiles = radar
|
||||
response.Range = attrib.Range
|
||||
response.Range = int32(attrib.Range)
|
||||
}
|
||||
|
||||
log.Printf("radar response \taccount:%s\tresponse:%+v\n", id, response)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// HandleRover handles the rover request
|
||||
func HandleRover(s *Server, vars map[string]string, b io.ReadCloser) (interface{}, error) {
|
||||
var response = rove.RoverResponse{}
|
||||
func (s *Server) Commands(ctx context.Context, req *rove.CommandsRequest) (*empty.Empty, error) {
|
||||
if len(req.Account) == 0 {
|
||||
return nil, fmt.Errorf("empty account")
|
||||
}
|
||||
resp, err := s.accountant.GetValue(ctx, &accounts.DataKey{Account: req.Account, Key: "rover"})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
id := vars["account"]
|
||||
key := accounts.DataKey{Account: id, Key: "rover"}
|
||||
if len(id) == 0 {
|
||||
return BadRequestError{Error: "no account ID provided"}, nil
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if resp, err := s.accountant.GetValue(ctx, &key); err != nil {
|
||||
return nil, fmt.Errorf("gRPC failed to contact accountant: %s", err)
|
||||
|
||||
} else if !resp.Success {
|
||||
return BadRequestError{Error: resp.Error}, nil
|
||||
|
||||
} else if id, err := uuid.Parse(resp.Value); err != nil {
|
||||
id, err := uuid.Parse(resp.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("account had invalid rover ID: %s", resp.Value)
|
||||
|
||||
} else if attrib, err := s.world.RoverAttributes(id); err != nil {
|
||||
return nil, fmt.Errorf("error getting rover attributes: %s", err)
|
||||
|
||||
} else {
|
||||
response.Attributes = attrib
|
||||
}
|
||||
|
||||
log.Printf("rover response \taccount:%s\tresponse:%+v\n", id, response)
|
||||
return response, nil
|
||||
var cmds []game.Command
|
||||
for _, c := range req.Commands {
|
||||
cmds = append(cmds, game.Command{
|
||||
Bearing: c.Bearing,
|
||||
Command: c.Command,
|
||||
Duration: int(c.Duration)})
|
||||
}
|
||||
|
||||
if err := s.world.Enqueue(id, cmds...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue