reorg
parent
cb42bdc8d0
commit
4bf83b3e40
|
|
@ -2,10 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"local/sandbox/blind-mans-poker/src/server/config"
|
||||
"local/sandbox/blind-mans-poker/src/server/game"
|
||||
"local/sandbox/blind-mans-poker/src/server/server"
|
||||
"local/sandbox/blind-mans-poker/src/server/storage"
|
||||
"local/sandbox/cards/src/config"
|
||||
"local/sandbox/cards/src/game"
|
||||
"local/sandbox/cards/src/server"
|
||||
"local/sandbox/cards/src/storage"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
|
@ -1,20 +1,27 @@
|
|||
package game
|
||||
|
||||
import (
|
||||
"local/sandbox/blind-mans-poker/src/server/config"
|
||||
"local/sandbox/blind-mans-poker/src/server/consts"
|
||||
"local/sandbox/blind-mans-poker/src/server/entity"
|
||||
"local/sandbox/blind-mans-poker/src/server/storage"
|
||||
"local/sandbox/cards/src/config"
|
||||
"local/sandbox/cards/src/consts"
|
||||
"local/sandbox/cards/src/entity"
|
||||
"local/sandbox/cards/src/storage"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Master struct {
|
||||
config config.Config
|
||||
storage *storage.Storage
|
||||
storage Storage
|
||||
locks *storage.RWLockMap
|
||||
}
|
||||
|
||||
func NewMaster(config config.Config, s *storage.Storage) *Master {
|
||||
type Storage interface {
|
||||
CreateGame(string) error
|
||||
GetGame(string) (entity.Game, error)
|
||||
ListGames() ([]string, error)
|
||||
ReplaceGame(string, entity.Game) error
|
||||
}
|
||||
|
||||
func NewMaster(config config.Config, s Storage) *Master {
|
||||
return &Master{
|
||||
config: config,
|
||||
storage: s,
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package game
|
||||
|
||||
import (
|
||||
"local/sandbox/blind-mans-poker/src/server/entity"
|
||||
"local/sandbox/cards/src/entity"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package game
|
||||
|
||||
type Rule interface {
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"local/router"
|
||||
"local/sandbox/cards/src/config"
|
||||
"local/sandbox/cards/src/entity"
|
||||
"local/sandbox/cards/src/game"
|
||||
"local/storage"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
config config.Config
|
||||
gm *game.Master
|
||||
*router.Router
|
||||
}
|
||||
|
||||
func NewServer(config config.Config, gm *game.Master) *Server {
|
||||
return &Server{
|
||||
config: config,
|
||||
gm: gm,
|
||||
Router: router.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func (server *Server) Routes() error {
|
||||
cases := map[string]map[string]http.HandlerFunc{
|
||||
fmt.Sprintf("%s/%s%s", server.config.Server.File.Prefix, router.Wildcard, router.Wildcard): map[string]http.HandlerFunc{
|
||||
http.MethodGet: server.File,
|
||||
},
|
||||
fmt.Sprintf("%s/games", server.config.Server.API.Prefix): map[string]http.HandlerFunc{
|
||||
http.MethodGet: server.GameList,
|
||||
},
|
||||
fmt.Sprintf("%s/games/%s", server.config.Server.API.Prefix, router.Wildcard): map[string]http.HandlerFunc{
|
||||
http.MethodGet: server.GameGet,
|
||||
http.MethodPost: server.GameInsert,
|
||||
http.MethodPut: server.GameReplace,
|
||||
},
|
||||
}
|
||||
|
||||
for path, spec := range cases {
|
||||
log.Println("listening for:", path)
|
||||
handler := server.ByMethod(spec)
|
||||
if err := server.Add(path, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (server *Server) ByMethod(m map[string]http.HandlerFunc) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
foo, ok := m[r.Method]
|
||||
if ok {
|
||||
foo(w, r)
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (server *Server) File(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
s := http.FileServer(http.Dir(server.config.Server.File.Root))
|
||||
r.URL.Path = strings.TrimPrefix(r.URL.Path, server.config.Server.File.Prefix)
|
||||
s.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (server *Server) GameList(w http.ResponseWriter, r *http.Request) {
|
||||
resp, err := server.gm.ListGames()
|
||||
if err != nil {
|
||||
internalError(w, err.Error())
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
func (server *Server) GameGet(w http.ResponseWriter, r *http.Request) {
|
||||
var gameID string
|
||||
err := router.Params(r, &gameID)
|
||||
if err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
resp, err := server.gm.GetGame(gameID)
|
||||
if err != nil {
|
||||
internalError(w, err.Error())
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
func (server *Server) GameReplace(w http.ResponseWriter, r *http.Request) {
|
||||
var gameID string
|
||||
err := router.Params(r, &gameID)
|
||||
if err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
var game entity.Game
|
||||
err = json.NewDecoder(r.Body).Decode(&game)
|
||||
if err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
err = server.gm.ReplaceGame(gameID, game)
|
||||
if err != nil {
|
||||
internalError(w, err.Error())
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(game)
|
||||
}
|
||||
|
||||
func (server *Server) GameInsert(w http.ResponseWriter, r *http.Request) {
|
||||
var gameID string
|
||||
err := router.Params(r, &gameID)
|
||||
if err != nil {
|
||||
badRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
err = server.gm.CreateGame(gameID)
|
||||
if err != nil {
|
||||
internalError(w, err.Error())
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(entity.Game{})
|
||||
}
|
||||
|
||||
func notImpl(w http.ResponseWriter) {
|
||||
errHandler(w, http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
|
||||
}
|
||||
|
||||
func internalError(w http.ResponseWriter, message string) {
|
||||
errHandler(w, http.StatusInternalServerError, message)
|
||||
}
|
||||
|
||||
func badRequest(w http.ResponseWriter, message string) {
|
||||
errHandler(w, http.StatusBadRequest, message)
|
||||
}
|
||||
|
||||
func errHandler(w http.ResponseWriter, status int, message string) {
|
||||
if message == storage.ErrNotFound.Error() {
|
||||
status = http.StatusNotFound
|
||||
}
|
||||
w.WriteHeader(status)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": http.StatusText(status),
|
||||
"statusCode": status,
|
||||
"error": message,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"local/sandbox/cards/src/config"
|
||||
"local/sandbox/cards/src/game"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerRouter(t *testing.T) {
|
||||
config := config.NewTestConfig(t)
|
||||
gm := game.NewTestMaster(t)
|
||||
server := NewServer(config, gm)
|
||||
if err := server.Routes(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
method string
|
||||
path string
|
||||
}{
|
||||
"file server root": {
|
||||
method: http.MethodGet,
|
||||
path: path.Join(server.config.Server.File.Prefix),
|
||||
},
|
||||
"api: games: get": {
|
||||
method: http.MethodGet,
|
||||
path: path.Join(server.config.Server.API.Prefix, "games"),
|
||||
},
|
||||
"api: games: id: get": {
|
||||
method: http.MethodGet,
|
||||
path: path.Join(server.config.Server.API.Prefix, "games", "my-game-id"),
|
||||
},
|
||||
"api: games: id: post": {
|
||||
method: http.MethodPost,
|
||||
path: path.Join(server.config.Server.API.Prefix, "games", "my-game-id"),
|
||||
},
|
||||
"api: games: id: put": {
|
||||
method: http.MethodPut,
|
||||
path: path.Join(server.config.Server.API.Prefix, "games", "my-game-id"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, d := range cases {
|
||||
c := d
|
||||
t.Run(name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(c.method, c.path, strings.NewReader("{}"))
|
||||
server.ServeHTTP(w, r)
|
||||
if w.Code == http.StatusNotFound && string(w.Body.Bytes()) == "404 page not found" {
|
||||
t.Fatalf("not found: (%s) %s: (%v) %s", c.method, c.path, w.Code, w.Body.Bytes())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerByMethod(t *testing.T) {
|
||||
server := NewServer(config.Config{}, nil)
|
||||
|
||||
gotGet := false
|
||||
get := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotGet = true })
|
||||
gotPost := false
|
||||
post := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotPost = true })
|
||||
|
||||
handler := server.ByMethod(map[string]http.HandlerFunc{
|
||||
http.MethodGet: get,
|
||||
http.MethodPost: post,
|
||||
})
|
||||
|
||||
handler(nil, &http.Request{Method: http.MethodGet})
|
||||
if !gotGet || gotPost {
|
||||
t.Fatal(gotGet, gotPost)
|
||||
}
|
||||
gotGet = false
|
||||
gotPost = false
|
||||
|
||||
handler(nil, &http.Request{Method: http.MethodPost})
|
||||
if gotGet || !gotPost {
|
||||
t.Fatal(gotGet, gotPost)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@ package storage
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"local/sandbox/blind-mans-poker/src/server/config"
|
||||
"local/sandbox/blind-mans-poker/src/server/entity"
|
||||
"local/sandbox/cards/src/config"
|
||||
"local/sandbox/cards/src/entity"
|
||||
"local/storage"
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue