Fix endpoint for getting lobby id from passphrase

Had to add several helpers (e.g. passphrase ones) to make the endpoint
for getting lobby id work.

Moved all handler functions into handler package.

Added test for getting lobby from passphrase.
This commit is contained in:
Marco 2024-05-09 22:29:48 +02:00
parent 11d6e8c98a
commit d7c4f28f3a
6 changed files with 206 additions and 107 deletions

View File

@ -1,25 +1,49 @@
package handler package handler
import ( import (
"log"
"mchess_server/api" "mchess_server/api"
"mchess_server/chess"
lobbies "mchess_server/lobby_registry" lobbies "mchess_server/lobby_registry"
"mchess_server/usher"
"mchess_server/utils" "mchess_server/utils"
"net/http" "net/http"
"sync"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid"
) )
func GetLobbyFromPassphraseHandler(c *gin.Context) { var mut sync.Mutex
reqPassphrase := api.Passphrase{}
err := c.ShouldBindJSON(&reqPassphrase) func HostPrivateGameHandler(c *gin.Context) {
if err != nil || reqPassphrase.Value == nil || *reqPassphrase.Value == "" { player := chess.NewPlayer(uuid.New())
c.IndentedJSON(http.StatusNotFound, reqPassphrase) u := usher.GetUsher()
mut.Lock()
defer mut.Unlock()
lobby := u.CreateNewPrivateLobby(player)
u.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
passphrase := lobby.Passphrase.String()
info := api.PlayerInfo{
PlayerID: &player.Uuid,
LobbyID: &lobby.Uuid,
Passphrase: &passphrase,
}
c.Header("Access-Control-Allow-Origin", "*")
c.IndentedJSON(http.StatusOK, info)
}
func GetLobbyForPassphraseHandler(c *gin.Context) {
reqPassphrase := c.Param("phrase")
if reqPassphrase == "" {
c.IndentedJSON(http.StatusBadRequest, reqPassphrase)
return return
} }
lobby := lobbies.GetLobbyRegistry().GetLobbyByPassphrase( passPhraseWithSpaces := utils.ConvertToPassphraseWithSpaces(reqPassphrase)
utils.NewPassphraseFromString(*reqPassphrase.Value)) lobby := lobbies.GetLobbyRegistry().GetLobbyByPassphrase(passPhraseWithSpaces)
if lobby == nil { if lobby == nil {
c.IndentedJSON(http.StatusNotFound, reqPassphrase) c.IndentedJSON(http.StatusNotFound, reqPassphrase)
@ -32,3 +56,73 @@ func GetLobbyFromPassphraseHandler(c *gin.Context) {
c.IndentedJSON(http.StatusOK, lobbyInfo) c.IndentedJSON(http.StatusOK, lobbyInfo)
} }
func RegisterForRandomGame(c *gin.Context) {
player := chess.NewPlayer(uuid.New())
usher := usher.GetUsher()
mut.Lock()
defer mut.Unlock()
lobby := usher.WelcomeNewPlayer(player)
usher.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
info := api.PlayerInfo{
PlayerID: &player.Uuid,
LobbyID: &lobby.Uuid,
}
log.Println("responding with info ", info)
c.Header("Access-Control-Allow-Origin", "*")
c.IndentedJSON(http.StatusOK, info)
}
func JoinPrivateGame(c *gin.Context) {
req := api.PlayerInfo{}
log.Println(c.Request.Body)
err := c.ShouldBindJSON(&req)
if err != nil || req.Passphrase == nil || *req.Passphrase == "" {
c.IndentedJSON(http.StatusNotFound, req)
}
u := usher.GetUsher()
if req.Passphrase != nil &&
*req.Passphrase != "" &&
req.PlayerID != nil &&
req.LobbyID != nil { //is reconnect
lobby := u.FindExistingPrivateLobby(utils.Passphrase(*req.Passphrase))
_, found := lobby.GetPlayerByUUID(*req.PlayerID)
if found {
c.IndentedJSON(
http.StatusOK,
api.PlayerInfo{
PlayerID: req.PlayerID,
LobbyID: req.LobbyID,
Passphrase: req.Passphrase,
})
return
} else {
c.IndentedJSON(http.StatusNotFound, req)
}
}
player := chess.NewPlayer(uuid.New())
mut.Lock()
defer mut.Unlock()
lobby := u.FindExistingPrivateLobby(utils.Passphrase(*req.Passphrase))
if lobby != nil {
u.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
} else {
c.IndentedJSON(http.StatusNotFound, req)
return
}
info := api.PlayerInfo{
PlayerID: &player.Uuid,
LobbyID: &lobby.Uuid,
Passphrase: req.Passphrase,
}
c.Header("Access-Control-Allow-Origin", "*")
c.IndentedJSON(http.StatusOK, info)
}

57
handler/handler_test.go Normal file
View File

@ -0,0 +1,57 @@
package handler
import (
"encoding/json"
"mchess_server/api"
"mchess_server/utils"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func Test_GetLobbyFromPassphraseHandler(t *testing.T) {
var passphraseURLParameter string
var hostedLobbyId uuid.UUID
t.Run("host a lobby", func(t *testing.T) {
r1 := httptest.NewRecorder()
ctx1, e1 := gin.CreateTestContext(r1)
e1.GET("/api/hostPrivate", HostPrivateGameHandler)
hostGameRequest, _ := http.NewRequest("GET", "/api/hostPrivate", nil)
ctx1.Request = hostGameRequest
e1.ServeHTTP(r1, hostGameRequest)
playerInfo := api.PlayerInfo{}
err := json.Unmarshal(r1.Body.Bytes(), &playerInfo)
assert.NoError(t, err)
receivedPhrase := *playerInfo.Passphrase
hostedLobbyId = *playerInfo.LobbyID
passphrase := utils.NewPassphraseFromString(receivedPhrase)
passphraseURLParameter = passphrase.AsURLParam()
})
t.Run("see if the lobby can be fetched by using the passphrase", func(t *testing.T) {
r2 := httptest.NewRecorder()
ctx2, engine := gin.CreateTestContext(r2)
engine.GET("/api/getLobbyForPassphrase/:phrase", GetLobbyForPassphraseHandler)
url := "/api/getLobbyForPassphrase/" + passphraseURLParameter
getLobbyRequest, _ := http.NewRequest("GET", url, nil)
ctx2.Request = getLobbyRequest
engine.ServeHTTP(r2, getLobbyRequest)
lobbyInfo := api.LobbyInfo{}
err := json.Unmarshal(r2.Body.Bytes(), &lobbyInfo)
assert.NoError(t, err)
assert.Equal(t, hostedLobbyId, *lobbyInfo.ID)
})
}

104
main.go
View File

@ -7,22 +7,16 @@ import (
"fmt" "fmt"
"log" "log"
"mchess_server/api" "mchess_server/api"
"mchess_server/chess" "mchess_server/handler"
lobbies "mchess_server/lobby_registry" lobbies "mchess_server/lobby_registry"
"mchess_server/usher"
"mchess_server/utils"
"net/http"
"sync"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid"
"nhooyr.io/websocket" "nhooyr.io/websocket"
) )
var cert_path = "/etc/letsencrypt/live/chess.sw-gross.de/" var cert_path = "/etc/letsencrypt/live/chess.sw-gross.de/"
var cert_file = cert_path + "fullchain.pem" var cert_file = cert_path + "fullchain.pem"
var key_file = cert_path + "privkey.pem" var key_file = cert_path + "privkey.pem"
var mut sync.Mutex
func main() { func main() {
var debugMode bool var debugMode bool
@ -35,10 +29,11 @@ func main() {
} }
router := gin.Default() router := gin.Default()
router.GET("/api/random", registerForRandomGame) router.GET("/api/random", handler.RegisterForRandomGame)
router.GET("/api/hostPrivate", hostPrivateGame) router.GET("/api/hostPrivate", handler.HostPrivateGameHandler)
router.POST("/api/joinPrivate", joinPrivateGame) router.POST("/api/joinPrivate", handler.JoinPrivateGame)
router.GET("/api/ws", registerWebSocketConnection) router.GET("/api/ws", registerWebSocketConnection)
router.GET("/api/getLobbyForPassphrase/:phrase", handler.GetLobbyForPassphraseHandler)
if debugMode { if debugMode {
log.Println("Starting service WITHOUT TLS") log.Println("Starting service WITHOUT TLS")
@ -51,95 +46,6 @@ func main() {
} }
} }
func registerForRandomGame(c *gin.Context) {
player := chess.NewPlayer(uuid.New())
usher := usher.GetUsher()
mut.Lock()
defer mut.Unlock()
lobby := usher.WelcomeNewPlayer(player)
usher.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
info := api.PlayerInfo{
PlayerID: &player.Uuid,
LobbyID: &lobby.Uuid,
}
log.Println("responding with info ", info)
c.Header("Access-Control-Allow-Origin", "*")
c.IndentedJSON(http.StatusOK, info)
}
func hostPrivateGame(c *gin.Context) {
player := chess.NewPlayer(uuid.New())
u := usher.GetUsher()
mut.Lock()
defer mut.Unlock()
lobby := u.CreateNewPrivateLobby(player)
u.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
passphrase := lobby.Passphrase.String()
info := api.PlayerInfo{
PlayerID: &player.Uuid,
LobbyID: &lobby.Uuid,
Passphrase: &passphrase,
}
c.Header("Access-Control-Allow-Origin", "*")
c.IndentedJSON(http.StatusOK, info)
}
func joinPrivateGame(c *gin.Context) {
req := api.PlayerInfo{}
log.Println(c.Request.Body)
err := c.ShouldBindJSON(&req)
if err != nil || req.Passphrase == nil || *req.Passphrase == "" {
c.IndentedJSON(http.StatusNotFound, req)
}
u := usher.GetUsher()
if req.Passphrase != nil &&
*req.Passphrase != "" &&
req.PlayerID != nil &&
req.LobbyID != nil { //is reconnect
lobby := u.FindExistingPrivateLobby(utils.Passphrase(*req.Passphrase))
_, found := lobby.GetPlayerByUUID(*req.PlayerID)
if found {
c.IndentedJSON(
http.StatusOK,
api.PlayerInfo{
PlayerID: req.PlayerID,
LobbyID: req.LobbyID,
Passphrase: req.Passphrase,
})
return
} else {
c.IndentedJSON(http.StatusNotFound, req)
}
}
player := chess.NewPlayer(uuid.New())
mut.Lock()
defer mut.Unlock()
lobby := u.FindExistingPrivateLobby(utils.Passphrase(*req.Passphrase))
if lobby != nil {
u.AddPlayerToLobbyAndStartGameIfFull(player, lobby)
} else {
c.IndentedJSON(http.StatusNotFound, req)
return
}
info := api.PlayerInfo{
PlayerID: &player.Uuid,
LobbyID: &lobby.Uuid,
Passphrase: req.Passphrase,
}
c.Header("Access-Control-Allow-Origin", "*")
c.IndentedJSON(http.StatusOK, info)
}
func registerWebSocketConnection(c *gin.Context) { func registerWebSocketConnection(c *gin.Context) {
webSocketConn, err := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{OriginPatterns: []string{"chess.sw-gross.de", "localhost:*"}}) webSocketConn, err := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{OriginPatterns: []string{"chess.sw-gross.de", "localhost:*"}})
if err != nil { if err != nil {

View File

@ -28006,7 +28006,6 @@ var CleanWords = []string{
"joystick", "joystick",
"joysticks", "joysticks",
"jpeg", "jpeg",
"jpn",
"juan", "juan",
"juana", "juana",
"juanita", "juanita",

View File

@ -2,6 +2,7 @@ package utils
import ( import (
"strings" "strings"
"unicode"
) )
type Passphrase string type Passphrase string
@ -24,6 +25,33 @@ func NewPassphraseFromString(s string) Passphrase {
return Passphrase(s) return Passphrase(s)
} }
func (p Passphrase) AsURLParam() string {
var result string
phraseAsString := p.String()
segments := strings.Split(phraseAsString, " ")
for _, segment := range segments {
runes := []rune(segment)
runes[0] = unicode.ToUpper(runes[0])
result += string(runes)
}
return result
}
func ConvertToPassphraseWithSpaces(s string) Passphrase {
result := ""
for _, rune := range s {
if unicode.IsUpper(rune) {
result += " "
}
result += strings.ToLower(string(rune))
}
return NewPassphraseFromString(strings.Trim(result, " "))
}
func (p Passphrase) String() string { func (p Passphrase) String() string {
return string(p) return string(p)
} }

View File

@ -19,3 +19,18 @@ func Test_onlyUniqueWords(t *testing.T) {
assert.Equal(t, numberOfCleanWords, len(wordsDistribution)) assert.Equal(t, numberOfCleanWords, len(wordsDistribution))
} }
func Test_Passphrase_AsURLParam(t *testing.T) {
phrase := NewPassphraseFromString("this is a Test phrase")
asParams := phrase.AsURLParam()
assert.Equal(t, "ThisIsATestPhrase", asParams)
}
func Test_Passphrase_ConvertToPassphraseWithSpaces(t *testing.T) {
fromURL := "ThisIsATestPhraseWithManyWords"
phrase := ConvertToPassphraseWithSpaces(fromURL)
assert.Equal(t, NewPassphraseFromString("this is a test phrase with many words"), phrase)
}