From 469f8665543bbc013a5f1bdcf63b38ede1c90a88 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 2 Mar 2024 02:38:26 +0100 Subject: [PATCH] Cleaning up examples in main and splitting the code into suitable packages --- backend/cmd/main.go | 40 +++++-------------- backend/internal/handlers/global_state.go | 36 +++++++++++++++++ .../internal/handlers/global_state_test.go | 15 +++++++ backend/internal/types/users.go | 23 +++++++++++ backend/internal/types/users_test.go | 35 ++++++++++++++++ 5 files changed, 120 insertions(+), 29 deletions(-) create mode 100644 backend/internal/handlers/global_state.go create mode 100644 backend/internal/handlers/global_state_test.go create mode 100644 backend/internal/types/users.go create mode 100644 backend/internal/types/users_test.go diff --git a/backend/cmd/main.go b/backend/cmd/main.go index b43b204..b238470 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -5,30 +5,12 @@ import ( "fmt" "ttime/internal/config" "ttime/internal/database" + "ttime/internal/handlers" "github.com/gofiber/fiber/v2" _ "github.com/mattn/go-sqlite3" ) -// The button state as represented in memory -type ButtonState struct { - PressCount int `json:"pressCount"` -} - -// This is what a handler with a receiver looks like -// Keep in mind that concurrent state access is not (usually) safe -// And will in practice be guarded by a mutex -func (b *ButtonState) pressHandlerGet(c *fiber.Ctx) error { - fmt.Println("Get request received") - return c.JSON(b) -} - -func (b *ButtonState) pressHandlerPost(c *fiber.Ctx) error { - fmt.Println("Post request received") - b.PressCount++ - return c.JSON(b) -} - func main() { conf, err := config.ReadConfigFromFile("config.toml") if err != nil { @@ -40,21 +22,21 @@ func main() { str, _ := json.MarshalIndent(conf, "", " ") fmt.Println(string(str)) - database.DbConnect(conf.DbPath) - + // Connect to the database + db := database.DbConnect(conf.DbPath) + // Get our global state + gs := handlers.NewGlobalState(db) + // Create the server server := fiber.New() + // Mount our static files (Beware of the security implications of this!) + // This will likely be replaced by an embedded filesystem in the future server.Static("/", "./static") - b := &ButtonState{PressCount: 0} - server.Get("/api/button", b.pressHandlerGet) - server.Post("/api/button", b.pressHandlerPost) - server.Post("/api/project", func(c *fiber.Ctx) error { - return c.JSON(fiber.Map{ - "message": "Project created", - }) - }) + // Register our handlers + server.Post("/api/register", gs.Register) + // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { fmt.Println("Error starting server: ", err) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go new file mode 100644 index 0000000..4dae3d8 --- /dev/null +++ b/backend/internal/handlers/global_state.go @@ -0,0 +1,36 @@ +package handlers + +import ( + "ttime/internal/database" + "ttime/internal/types" + + "github.com/gofiber/fiber/v2" +) + +// The actual interface that we will use +type GlobalState interface { + Register(c *fiber.Ctx) error +} + +// "Constructor" +func NewGlobalState(db database.Database) GlobalState { + return &GState{Db: db} +} + +// The global state, which implements all the handlers +type GState struct { + Db database.Database +} + +func (gs *GState) Register(c *fiber.Ctx) error { + u := new(types.User) + if err := c.BodyParser(u); err != nil { + return c.Status(400).SendString(err.Error()) + } + + if err := gs.Db.AddUser(u.Username, u.Password); err != nil { + return c.Status(500).SendString(err.Error()) + } + + return c.Status(200).SendString("User added") +} diff --git a/backend/internal/handlers/global_state_test.go b/backend/internal/handlers/global_state_test.go new file mode 100644 index 0000000..c0b64f7 --- /dev/null +++ b/backend/internal/handlers/global_state_test.go @@ -0,0 +1,15 @@ +package handlers + +import ( + "testing" + "ttime/internal/database" +) + +// The actual interface that we will use +func TestGlobalState(t *testing.T) { + db := database.DbConnect(":memory:") + gs := NewGlobalState(db) + if gs == nil { + t.Error("NewGlobalState returned nil") + } +} diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go new file mode 100644 index 0000000..fa735d7 --- /dev/null +++ b/backend/internal/types/users.go @@ -0,0 +1,23 @@ +package types + +// User struct represents a user in the system +type User struct { + UserId string `json:"userId"` + Username string `json:"username"` + Password string `json:"password"` +} + +// If the user needs to be served over the api, we dont want to send the password +// ToPublicUser converts a User to a PublicUser +func (u *User) ToPublicUser() (*PublicUser, error) { + return &PublicUser{ + UserId: u.UserId, + Username: u.Username, + }, nil +} + +// PublicUser represents a user that is safe to send over the API (no password) +type PublicUser struct { + UserId string `json:"userId"` + Username string `json:"username"` +} diff --git a/backend/internal/types/users_test.go b/backend/internal/types/users_test.go new file mode 100644 index 0000000..811839d --- /dev/null +++ b/backend/internal/types/users_test.go @@ -0,0 +1,35 @@ +package types + +import "testing" + +// NewUser returns a new User +func TestNewUser(t *testing.T) { + u := User{ + UserId: "123", + Username: "test", + Password: "password", + } + + if u.UserId != "123" { + t.Errorf("Expected user id to be 123, got %s", u.UserId) + } + if u.Username != "test" { + t.Errorf("Expected username to be test, got %s", u.Username) + } + if u.Password != "password" { + t.Errorf("Expected password to be password, got %s", u.Password) + } +} + +func TestToPublicUser(t *testing.T) { + u := User{ + UserId: "123", + Username: "test", + Password: "password", + } + + _, err := u.ToPublicUser() + if err != nil { + t.Errorf("Expected no error, got %s", err) + } +}