diff --git a/api/lobby_info.go b/api/lobby_info.go new file mode 100644 index 0000000..149bf20 --- /dev/null +++ b/api/lobby_info.go @@ -0,0 +1,7 @@ +package api + +import "github.com/google/uuid" + +type LobbyInfo struct { + ID *uuid.UUID `json:"id,omitempty"` +} diff --git a/api/passphrase.go b/api/passphrase.go new file mode 100644 index 0000000..c50b803 --- /dev/null +++ b/api/passphrase.go @@ -0,0 +1,5 @@ +package api + +type Passphrase struct { + Value *string `json:"value,omitempty"` +} diff --git a/go.mod b/go.mod index 4477e2a..901fad6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module mchess_server go 1.22 require ( - github.com/gin-gonic/gin v1.9.1 + github.com/gin-gonic/gin v1.10.0 github.com/google/uuid v1.6.0 github.com/samber/lo v1.39.0 github.com/stretchr/testify v1.9.0 @@ -13,8 +13,6 @@ require ( require ( github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -24,23 +22,22 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect - golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/protobuf v1.34.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 04ed76e..81cc336 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -40,6 +42,7 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -62,6 +65,8 @@ github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeB github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg= github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= @@ -85,24 +90,38 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handler/handler.go b/handler/handler.go new file mode 100644 index 0000000..6615f86 --- /dev/null +++ b/handler/handler.go @@ -0,0 +1,128 @@ +package handler + +import ( + "log" + "mchess_server/api" + "mchess_server/chess" + lobbies "mchess_server/lobby_registry" + "mchess_server/usher" + "mchess_server/utils" + "net/http" + "sync" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" +) + +var mut sync.Mutex + +func HostPrivateGameHandler(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 GetLobbyForPassphraseHandler(c *gin.Context) { + reqPassphrase := c.Param("phrase") + if reqPassphrase == "" { + c.IndentedJSON(http.StatusBadRequest, reqPassphrase) + return + } + + passPhraseWithSpaces := utils.ConvertToPassphraseWithSpaces(reqPassphrase) + lobby := lobbies.GetLobbyRegistry().GetLobbyByPassphrase(passPhraseWithSpaces) + + if lobby == nil { + c.IndentedJSON(http.StatusNotFound, reqPassphrase) + return + } + + lobbyInfo := api.LobbyInfo{ + ID: &lobby.Uuid, + } + + 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) +} diff --git a/handler/handler_test.go b/handler/handler_test.go new file mode 100644 index 0000000..8e69445 --- /dev/null +++ b/handler/handler_test.go @@ -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) + }) +} diff --git a/main.go b/main.go index a2432ef..5c7eb8f 100644 --- a/main.go +++ b/main.go @@ -7,22 +7,16 @@ import ( "fmt" "log" "mchess_server/api" - "mchess_server/chess" + "mchess_server/handler" lobbies "mchess_server/lobby_registry" - "mchess_server/usher" - "mchess_server/utils" - "net/http" - "sync" "github.com/gin-gonic/gin" - "github.com/google/uuid" "nhooyr.io/websocket" ) var cert_path = "/etc/letsencrypt/live/chess.sw-gross.de/" var cert_file = cert_path + "fullchain.pem" var key_file = cert_path + "privkey.pem" -var mut sync.Mutex func main() { var debugMode bool @@ -35,10 +29,11 @@ func main() { } router := gin.Default() - router.GET("/api/random", registerForRandomGame) - router.GET("/api/hostPrivate", hostPrivateGame) - router.POST("/api/joinPrivate", joinPrivateGame) + router.GET("/api/random", handler.RegisterForRandomGame) + router.GET("/api/hostPrivate", handler.HostPrivateGameHandler) + router.POST("/api/joinPrivate", handler.JoinPrivateGame) router.GET("/api/ws", registerWebSocketConnection) + router.GET("/api/getLobbyForPassphrase/:phrase", handler.GetLobbyForPassphraseHandler) if debugMode { 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) { webSocketConn, err := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{OriginPatterns: []string{"chess.sw-gross.de", "localhost:*"}}) if err != nil { diff --git a/utils/clean-words.go b/utils/clean-words.go index 6dda176..2e32f0f 100644 --- a/utils/clean-words.go +++ b/utils/clean-words.go @@ -28006,7 +28006,6 @@ var CleanWords = []string{ "joystick", "joysticks", "jpeg", - "jpn", "juan", "juana", "juanita", diff --git a/utils/passphrase.go b/utils/passphrase.go index d0762d0..7311667 100644 --- a/utils/passphrase.go +++ b/utils/passphrase.go @@ -2,6 +2,7 @@ package utils import ( "strings" + "unicode" ) type Passphrase string @@ -20,6 +21,37 @@ func NewPassphrase() Passphrase { return Passphrase(strings.TrimSpace(phrase)) } +func NewPassphraseFromString(s string) Passphrase { + 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 { return string(p) } diff --git a/utils/passphrase_test.go b/utils/passphrase_test.go index 5876bfa..9af9048 100644 --- a/utils/passphrase_test.go +++ b/utils/passphrase_test.go @@ -19,3 +19,18 @@ func Test_onlyUniqueWords(t *testing.T) { 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) +}