From 76fefd2b2482a4aee882dff68e7a73fb26aa9980 Mon Sep 17 00:00:00 2001 From: dDogge Date: Mon, 18 Mar 2024 13:32:55 +0100 Subject: [PATCH 01/25] Added function to check if someone is admin --- backend/internal/database/db.go | 21 ++++++++++++++ backend/internal/database/db_test.go | 43 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5cbb13f..82a3551 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -32,6 +32,7 @@ type Database interface { GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) SignWeeklyReport(reportId int, projectManagerId int) error + IsSiteAdmin(username string) (bool, error) } // This struct is a wrapper type that holds the database connection @@ -313,6 +314,26 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { return err } +// IsSiteAdmin checks if a given username is a site admin +func (d *Db) IsSiteAdmin(username string) (bool, error) { + // Define the SQL query to check if the user is a site admin + query := ` + SELECT COUNT(*) FROM site_admin + JOIN users ON site_admin.admin_id = users.id + WHERE users.username = ? + ` + + // Execute the query + var count int + err := d.Get(&count, query, username) + if err != nil { + return false, err + } + + // If count is greater than 0, the user is a site admin + return count > 0, nil +} + // Reads a directory of migration files and applies them to the database. // This will eventually be used on an embedded directory func (d *Db) Migrate() error { diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 09de45b..2378b3d 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -536,3 +536,46 @@ func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) { t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't") } } + +func TestIsSiteAdmin(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add a site admin + err = db.AddUser("admin", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Promote the user to site admin + err = db.PromoteToAdmin("admin") + if err != nil { + t.Error("PromoteToAdmin failed:", err) + } + + // Check if the user is a site admin + isAdmin, err := db.IsSiteAdmin("admin") + if err != nil { + t.Error("IsSiteAdmin failed:", err) + } + if !isAdmin { + t.Error("IsSiteAdmin failed: expected true, got false") + } + + // Add a regular user + err = db.AddUser("regularuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Check if the regular user is not a site admin + isRegularUserAdmin, err := db.IsSiteAdmin("regularuser") + if err != nil { + t.Error("IsSiteAdmin failed:", err) + } + if isRegularUserAdmin { + t.Error("IsSiteAdmin failed: expected false, got true") + } +} From 409d973d8acc9402d5f539155eb6d177bccbfa1c Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Mon, 18 Mar 2024 14:35:56 +0100 Subject: [PATCH 02/25] minor fix --- frontend/src/Pages/YourProjectsPage.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 2b3fd18..6de09ef 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,6 +1,6 @@ import BasicWindow from "../Components/BasicWindow"; import { ProjectListUser } from "../Components/ProjectListUser"; -import { Project } from "../Types/Project"; +import { Project } from "../Types/goTypes"; function YourProjectsPage(): JSX.Element { //TODO: Change so that it reads projects from database @@ -10,7 +10,6 @@ function YourProjectsPage(): JSX.Element { id: i, name: "Example Project " + i, description: "good", - created: "now", owner: "me", }); } From d6ce4a3c57851f9f537f3432a581329a9a17d8a0 Mon Sep 17 00:00:00 2001 From: al8763be Date: Mon, 18 Mar 2024 14:43:28 +0100 Subject: [PATCH 03/25] errMessage fixed --- frontend/src/Components/Register.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index d8b2a47..facca39 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -23,6 +23,7 @@ export default function Register(): JSX.Element { nav("/"); // Instantly navigate to the login page } else { setErrMessage(response.message ?? "Unknown error"); + console.error(errMessage); } }; From 49793787792171b72450b0825deac96b40b10a32 Mon Sep 17 00:00:00 2001 From: dDogge Date: Mon, 18 Mar 2024 14:47:15 +0100 Subject: [PATCH 04/25] Handler for AddUserToProject and promoteToAdmin, successfully tested --- backend/internal/handlers/global_state.go | 2 + .../handlers/handlers_project_related.go | 42 +++++++++++++++++++ .../handlers/handlers_user_related.go | 23 ++++++++++ backend/main.go | 3 ++ 4 files changed, 70 insertions(+) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index c8beb1c..ef7e48d 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -17,6 +17,8 @@ type GlobalState interface { SubmitWeeklyReport(c *fiber.Ctx) error GetWeeklyReport(c *fiber.Ctx) error SignReport(c *fiber.Ctx) error + AddUserToProjectHandler(c *fiber.Ctx) error + PromoteToAdmin(c *fiber.Ctx) error // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project // DeleteProject(c *fiber.Ctx) error // To delete a project diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index 6a430e9..74f57f7 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -96,3 +96,45 @@ func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error { // Return the list of users as JSON return c.JSON(users) } + +// AddUserToProjectHandler is a handler that adds a user to a project with a specified role +func (gs *GState) AddUserToProjectHandler(c *fiber.Ctx) error { + // Extract necessary parameters from the request + var requestData struct { + Username string `json:"username"` + ProjectName string `json:"projectName"` + Role string `json:"role"` + } + if err := c.BodyParser(&requestData); err != nil { + println("Error parsing request body:", err) + return c.Status(400).SendString("Bad request") + } + + // Check if the user adding another user to the project is a site admin + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + adminUsername := claims["name"].(string) + println("Admin username from claims:", adminUsername) + + isAdmin, err := gs.Db.IsSiteAdmin(adminUsername) + if err != nil { + println("Error checking admin status:", err) + return c.Status(500).SendString(err.Error()) + } + + if !isAdmin { + println("User is not a site admin:", adminUsername) + return c.Status(403).SendString("User is not a site admin") + } + + // Add the user to the project with the specified role + err = gs.Db.AddUserToProject(requestData.Username, requestData.ProjectName, requestData.Role) + if err != nil { + println("Error adding user to project:", err) + return c.Status(500).SendString(err.Error()) + } + + // Return success message + println("User added to project successfully:", requestData.Username) + return c.SendStatus(fiber.StatusOK) +} diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 0619ea5..0f7c047 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -1,6 +1,7 @@ package handlers import ( + "fmt" "time" "ttime/internal/types" @@ -122,3 +123,25 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { // Return the list of users as JSON return c.JSON(users) } + +func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { + // Extract the username from the request body + var newUser types.NewUser + if err := c.BodyParser(&newUser); err != nil { + return c.Status(400).SendString("Bad request") + } + username := newUser.Username + + println("Promoting user to admin:", username) // Debug print + + // Promote the user to a site admin in the database + if err := gs.Db.PromoteToAdmin(username); err != nil { + fmt.Println("Error promoting user to admin:", err) // Debug print + return c.Status(500).SendString(err.Error()) + } + + println("User promoted to admin successfully:", username) // Debug print + + // Return a success message + return c.SendStatus(fiber.StatusOK) +} diff --git a/backend/main.go b/backend/main.go index bc33942..7cf6c9f 100644 --- a/backend/main.go +++ b/backend/main.go @@ -79,6 +79,9 @@ func main() { server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Post("/api/project", gs.CreateProject) server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) + server.Post("/api/signReport", gs.SignReport) + server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) + server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) From 3a3690e3da72fcc2e173520006defdd6f9054b04 Mon Sep 17 00:00:00 2001 From: borean Date: Mon, 18 Mar 2024 15:12:11 +0100 Subject: [PATCH 05/25] ignore go.work.sum --- .gitignore | 1 + go.work.sum | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index bdbfff8..313b735 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ dist/ .vscode/ .idea/ .DS_Store +.go.work.sum # Ignore configuration files .env diff --git a/go.work.sum b/go.work.sum index a58340b..087c85b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,15 +1,28 @@ -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 9ad89d60636ac6091d71b0bf307982becc9b89fe Mon Sep 17 00:00:00 2001 From: dDogge Date: Mon, 18 Mar 2024 16:01:00 +0100 Subject: [PATCH 06/25] Handler for SignReport added and corresponding test in testing.pu added --- .../handlers/handlers_report_related.go | 32 +++-- testing.py | 124 +++++++++++++++++- 2 files changed, 141 insertions(+), 15 deletions(-) diff --git a/backend/internal/handlers/handlers_report_related.go b/backend/internal/handlers/handlers_report_related.go index 509bd67..291d068 100644 --- a/backend/internal/handlers/handlers_report_related.go +++ b/backend/internal/handlers/handlers_report_related.go @@ -64,30 +64,42 @@ func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error { return c.JSON(report) } +type ReportId struct { + ReportId int +} + func (gs *GState) SignReport(c *fiber.Ctx) error { + println("Signing report...") // Extract the necessary parameters from the token user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) - managerUsername := claims["name"].(string) + projectManagerUsername := claims["name"].(string) - // Extract the report ID and project manager ID from request parameters - reportID, err := strconv.Atoi(c.Params("reportId")) - if err != nil { - return c.Status(400).SendString("Invalid report ID") + // Extract report ID from the request query parameters + // reportID := c.Query("reportId") + rid := new(ReportId) + if err := c.BodyParser(rid); err != nil { + return err } + println("Signing report for: ", rid.ReportId) + // reportIDInt, err := strconv.Atoi(rid.ReportId) + // println("Signing report for: ", rid.ReportId) + // if err != nil { + // return c.Status(400).SendString("Invalid report ID") + // } - // Call the database function to get the project manager ID - managerID, err := gs.Db.GetUserId(managerUsername) + // Get the project manager's ID + projectManagerID, err := gs.Db.GetUserId(projectManagerUsername) if err != nil { return c.Status(500).SendString("Failed to get project manager ID") } + println("blabla", projectManagerID) // Call the database function to sign the weekly report - err = gs.Db.SignWeeklyReport(reportID, managerID) + err = gs.Db.SignWeeklyReport(rid.ReportId, projectManagerID) if err != nil { - return c.Status(500).SendString("Failed to sign the weekly report: " + err.Error()) + return c.Status(500).SendString(err.Error()) } - // Return success response return c.Status(200).SendString("Weekly report signed successfully") } diff --git a/testing.py b/testing.py index 38d09cc..164c64e 100644 --- a/testing.py +++ b/testing.py @@ -22,6 +22,9 @@ loginPath = base_url + "/api/login" addProjectPath = base_url + "/api/project" submitReportPath = base_url + "/api/submitReport" getWeeklyReportPath = base_url + "/api/getWeeklyReport" +signReportPath = base_url + "/api/signReport" +addUserToProjectPath = base_url + "/api/addUserToProject" +promoteToAdminPath = base_url + "/api/promoteToAdmin" # Posts the username and password to the register endpoint @@ -43,20 +46,20 @@ def login(username: string, password: string): print(response.text) return response - +# Test function to login def test_login(): response = login(username, "always_same") assert response.status_code == 200, "Login failed" print("Login successful") return response.json()["token"] - +# Test function to create a new user def test_create_user(): response = register(username, "always_same") assert response.status_code == 200, "Registration failed" print("Registration successful") - +# Test function to add a project def test_add_project(): loginResponse = login(username, "always_same") token = loginResponse.json()["token"] @@ -69,7 +72,7 @@ def test_add_project(): assert response.status_code == 200, "Add project failed" print("Add project successful") - +# Test function to submit a report def test_submit_report(): token = login(username, "always_same").json()["token"] response = requests.post( @@ -90,6 +93,7 @@ def test_submit_report(): assert response.status_code == 200, "Submit report failed" print("Submit report successful") +# Test function to get a weekly report def test_get_weekly_report(): token = login(username, "always_same").json()["token"] response = requests.get( @@ -99,9 +103,119 @@ def test_get_weekly_report(): ) print(response.text) +# Test function to add a user to a project +def test_add_user_to_project(): + # Log in as a site admin + admin_username = randomString() + admin_password = "admin_password" + print("Registering with username: ", admin_username, " and password: ", admin_password) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + print(response.text) + + admin_token = login(admin_username, admin_password).json()["token"] + response = requests.post(promoteToAdminPath, json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token}) + print(response.text) + assert response.status_code == 200, "Promote to site admin failed" + print("Admin promoted to site admin successfully") + + # Create a new user to add to the project + new_user = randomString() + register(new_user, "new_user_password") + + # Add the new user to the project as a member + response = requests.put( + addUserToProjectPath, + json={"projectName": projectName, "username": new_user, "role": "member"}, + headers={"Authorization": "Bearer " + admin_token}, + ) + + print(response.text) + assert response.status_code == 200, "Add user to project failed" + print("Add user to project successful") + +# Test function to sign a report +def test_sign_report(): + # Create a project manager user + project_manager = randomString() + register(project_manager, "project_manager_password") + + # Register an admin + admin_username = randomString() + admin_password = "admin_password2" + print("Registering with username: ", admin_username, " and password: ", admin_password) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + print(response.text) + + # Log in as the admin + admin_token = login(admin_username, admin_password).json()["token"] + response = requests.post(promoteToAdminPath, json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token}) + + response = requests.put( + addUserToProjectPath, + json={"projectName": projectName, "username": project_manager, "role": "project_manager"}, + headers={"Authorization": "Bearer " + admin_token}, + ) + assert response.status_code == 200, "Add project manager to project failed" + print("Project manager added to project successfully") + + # Log in as the project manager + project_manager_token = login(project_manager, "project_manager_password").json()["token"] + + # Submit a report for the project + token = login(username, "always_same").json()["token"] + response = requests.post( + submitReportPath, + json={ + "projectName": projectName, + "week": 1, + "developmentTime": 10, + "meetingTime": 5, + "adminTime": 5, + "ownWorkTime": 10, + "studyTime": 10, + "testingTime": 10, + }, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Submit report failed" + print("Submit report successful") + + # Retrieve the report ID + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName , "week": 1} + ) + print(response.text) + report_id = response.json()["reportId"] + + # Sign the report as the project manager + response = requests.post( + signReportPath, + json={"reportId": report_id}, + headers={"Authorization": "Bearer " + project_manager_token}, + ) + assert response.status_code == 200, "Sign report failed" + print("Sign report successful") + + # Retrieve the report ID again for confirmation + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName , "week": 1} + ) + print(response.text) + + if __name__ == "__main__": test_create_user() test_login() test_add_project() test_submit_report() - test_get_weekly_report() \ No newline at end of file + test_get_weekly_report() + test_sign_report() + test_add_user_to_project() \ No newline at end of file From 0c2617d0cb9dd03a73005bb6347d16ac7fd3ddc1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 18 Mar 2024 16:42:35 +0100 Subject: [PATCH 07/25] Full fix for getProject route, testing, integration testing and frontend-API code --- backend/internal/database/db.go | 5 +++- backend/internal/database/db_test.go | 30 +++++++++++++++++++ backend/internal/handlers/global_state.go | 1 + .../handlers/handlers_project_related.go | 5 ++++ backend/main.go | 1 + frontend/src/API/API.ts | 27 +++++++++++++++++ testing.py | 15 +++++++++- 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5cbb13f..a5360e6 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -106,7 +106,10 @@ func (d *Db) GetAllProjects() ([]types.Project, error) { // GetProject retrieves a specific project by its ID. func (d *Db) GetProject(projectId int) (types.Project, error) { var project types.Project - err := d.Select(&project, "SELECT * FROM projects WHERE id = ?") + err := d.Get(&project, "SELECT * FROM projects WHERE id = ?", projectId) + if err != nil { + println("Error getting project: ", err) + } return project, err } diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 09de45b..a7f3878 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -536,3 +536,33 @@ func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) { t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't") } } + +func TestGetProject(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add a user + err = db.AddUser("testuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add a project + err = db.AddProject("testproject", "description", "testuser") + if err != nil { + t.Error("AddProject failed:", err) + } + + // Retrieve the added project + project, err := db.GetProject(1) + if err != nil { + t.Error("GetProject failed:", err) + } + + // Check if the retrieved project matches the expected values + if project.Name != "testproject" { + t.Errorf("Expected Name to be testproject, got %s", project.Name) + } +} diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index c8beb1c..92bc19d 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -17,6 +17,7 @@ type GlobalState interface { SubmitWeeklyReport(c *fiber.Ctx) error GetWeeklyReport(c *fiber.Ctx) error SignReport(c *fiber.Ctx) error + GetProject(c *fiber.Ctx) error // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project // DeleteProject(c *fiber.Ctx) error // To delete a project diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index 6a430e9..d779663 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -66,6 +66,10 @@ func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error { func (gs *GState) GetProject(c *fiber.Ctx) error { // Extract the project ID from the request parameters or body projectID := c.Params("projectID") + if projectID == "" { + return c.Status(400).SendString("No project ID provided") + } + println("Getting project with ID: ", projectID) // Parse the project ID into an integer projectIDInt, err := strconv.Atoi(projectID) @@ -80,6 +84,7 @@ func (gs *GState) GetProject(c *fiber.Ctx) error { } // Return the project as JSON + println("Returning project: ", project.Name) return c.JSON(project) } diff --git a/backend/main.go b/backend/main.go index bc33942..920696b 100644 --- a/backend/main.go +++ b/backend/main.go @@ -78,6 +78,7 @@ func main() { server.Post("/api/loginrenew", gs.LoginRenew) server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches server.Post("/api/project", gs.CreateProject) + server.Get("/api/project/:projectId", gs.GetProject) server.Get("/api/getWeeklyReport", gs.GetWeeklyReport) // Announce the port we are listening on and start the server diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index ac0f531..e8566b6 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -46,6 +46,7 @@ interface API { username: string, token: string, ): Promise>; + getProject(id: number): Promise>; } // Export an instance of the API @@ -253,4 +254,30 @@ export const api: API = { return Promise.resolve({ success: false, message: "Failed to login" }); } }, + + // Gets a projet by id, currently untested since we have no javascript-based tests + async getProject(id: number): Promise> { + try { + const response = await fetch(`/api/project/${id}`, { + method: "GET", + }); + + if (!response.ok) { + return { + success: false, + message: "Failed to get project: Response code " + response.status, + }; + } else { + const data = (await response.json()) as Project; + return { success: true, data }; + } + // The code below is garbage but satisfies the linter + // This needs fixing, do not copy this pattern + } catch (e: unknown) { + return { + success: false, + message: "Failed to get project: " + (e as Error).toString(), + }; + } + }, }; diff --git a/testing.py b/testing.py index 38d09cc..fd4ec74 100644 --- a/testing.py +++ b/testing.py @@ -22,6 +22,7 @@ loginPath = base_url + "/api/login" addProjectPath = base_url + "/api/project" submitReportPath = base_url + "/api/submitReport" getWeeklyReportPath = base_url + "/api/getWeeklyReport" +getProjectPath = base_url + "/api/project" # Posts the username and password to the register endpoint @@ -98,10 +99,22 @@ def test_get_weekly_report(): params={"username": username, "projectName": projectName , "week": 1} ) print(response.text) + assert response.status_code == 200, "Get weekly report failed" + +# Tests getting a project by id +def test_get_project(): + token = login(username, "always_same").json()["token"] + response = requests.get( + getProjectPath + "/1", # Assumes that the project with id 1 exists + headers={"Authorization": "Bearer " + token}, + ) + print(response.text) + assert response.status_code == 200, "Get project failed" if __name__ == "__main__": test_create_user() test_login() test_add_project() test_submit_report() - test_get_weekly_report() \ No newline at end of file + test_get_weekly_report() + test_get_project() \ No newline at end of file From 576a137038a7ec2bd41bb6bb10eeacaecdf3ee4b Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Mon, 18 Mar 2024 16:00:08 +0100 Subject: [PATCH 08/25] new component --- frontend/src/Components/AdminUserList.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 frontend/src/Components/AdminUserList.tsx diff --git a/frontend/src/Components/AdminUserList.tsx b/frontend/src/Components/AdminUserList.tsx new file mode 100644 index 0000000..e69de29 From 8291f4caf30725bd83c12269e3ba7f2da19ef170 Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Mon, 18 Mar 2024 16:11:24 +0100 Subject: [PATCH 09/25] delete component --- frontend/src/Components/AdminUserList.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 frontend/src/Components/AdminUserList.tsx diff --git a/frontend/src/Components/AdminUserList.tsx b/frontend/src/Components/AdminUserList.tsx deleted file mode 100644 index e69de29..0000000 From 22b9580f5149fc9eb3d083427ca16f41c17b76e6 Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Mon, 18 Mar 2024 16:13:47 +0100 Subject: [PATCH 10/25] fix backbutton --- frontend/src/Pages/AdminPages/AdminAddUser.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/frontend/src/Pages/AdminPages/AdminAddUser.tsx b/frontend/src/Pages/AdminPages/AdminAddUser.tsx index c0f9492..c736e4d 100644 --- a/frontend/src/Pages/AdminPages/AdminAddUser.tsx +++ b/frontend/src/Pages/AdminPages/AdminAddUser.tsx @@ -1,5 +1,5 @@ +import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; import Register from "../../Components/Register"; function AdminAddUser(): JSX.Element { @@ -11,13 +11,7 @@ function AdminAddUser(): JSX.Element { const buttons = ( <> -