diff --git a/.gitignore b/.gitignore index c50fe24..281e866 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ diagram.puml backend/*.png backend/*.jpg backend/*.svg -__pycache__ /go.work.sum /package-lock.json diff --git a/backend/docs/docs.go b/backend/docs/docs.go index c8b020d..7a08b0e 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -108,56 +108,6 @@ const docTemplate = `{ } } }, - "/promote/{projectName}": { - "put": { - "security": [ - { - "JWT": [] - } - ], - "description": "Promote a user to project manager", - "consumes": [ - "text/plain" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "Auth" - ], - "summary": "Promote to project manager", - "parameters": [ - { - "type": "string", - "description": "Project name", - "name": "projectName", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User name", - "name": "userName", - "in": "query", - "required": true - } - ], - "responses": { - "403": { - "description": "Forbidden", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, "/promoteToAdmin": { "post": { "security": [ diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 22e11e9..f4c0f6e 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -17,7 +17,6 @@ type Database interface { AddUser(username string, password string) error CheckUser(username string, password string) bool RemoveUser(username string) error - RemoveUserFromProject(username string, projectname string) error PromoteToAdmin(username string) error GetUserId(username string) (int, error) AddProject(name string, description string, username string) error @@ -36,7 +35,7 @@ type Database interface { GetProject(projectId int) (types.Project, error) GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) - GetAllWeeklyReports(username string, projectname string) ([]types.WeeklyReportList, error) + GetWeeklyReportsUser(username string, projectname string) ([]types.WeeklyReportList, error) GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) SignWeeklyReport(reportId int, projectManagerId int) error IsSiteAdmin(username string) (bool, error) @@ -44,7 +43,6 @@ type Database interface { GetProjectTimes(projectName string) (map[string]int, error) UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error RemoveProject(projectname string) error - GetUserName(id int) (string, error) } // This struct is a wrapper type that holds the database connection @@ -88,10 +86,6 @@ const isProjectManagerQuery = `SELECT COUNT(*) > 0 FROM user_roles JOIN projects ON user_roles.project_id = projects.id WHERE users.username = ? AND projects.name = ? AND user_roles.p_role = 'project_manager'` -const removeUserFromProjectQuery = `DELETE FROM user_roles - WHERE user_id = (SELECT id FROM users WHERE username = ?) - AND project_id = (SELECT id FROM projects WHERE name = ?)` - // DbConnect connects to the database func DbConnect(dbpath string) Database { // Open the database @@ -153,11 +147,6 @@ func (d *Db) AddUserToProject(username string, projectname string, role string) return err } -func (d *Db) RemoveUserFromProject(username string, projectname string) error { - _, err := d.Exec(removeUserFromProjectQuery, username, projectname) - return err -} - // ChangeUserRole changes the role of a user within a project. func (d *Db) ChangeUserRole(username string, projectname string, role string) error { // Execute the SQL query to change the user's role @@ -350,14 +339,9 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { return err } - managerQuery := `SELECT project_id FROM user_roles - WHERE user_id = ? - AND project_id = (SELECT project_id FROM weekly_reports WHERE report_id = ?) - AND p_role = 'project_manager'` - // Retrieve the project ID associated with the project manager var managerProjectID int - err = d.Get(&managerProjectID, managerQuery, projectManagerId, reportId) + err = d.Get(&managerProjectID, "SELECT project_id FROM user_roles WHERE user_id = ? AND p_role = 'project_manager'", projectManagerId) if err != nil { return err } @@ -479,8 +463,8 @@ func (d *Db) Migrate() error { return nil } -// GetAllWeeklyReports retrieves weekly reports for a specific user and project. -func (d *Db) GetAllWeeklyReports(username string, projectName string) ([]types.WeeklyReportList, error) { +// GetWeeklyReportsUser retrieves weekly reports for a specific user and project. +func (d *Db) GetWeeklyReportsUser(username string, projectName string) ([]types.WeeklyReportList, error) { query := ` SELECT wr.week, @@ -617,9 +601,3 @@ func (d *Db) RemoveProject(projectname string) error { _, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname) return err } - -func (d *Db) GetUserName(id int) (string, error) { - var username string - err := d.Get(&username, "SELECT username FROM users WHERE id = ?", id) - return username, err -} diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index a691a4d..fe3e6cd 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -705,7 +705,7 @@ func TestGetWeeklyReportsUser(t *testing.T) { t.Error("AddWeeklyReport failed:", err) } - reports, err := db.GetAllWeeklyReports("testuser", "testproject") + reports, err := db.GetWeeklyReportsUser("testuser", "testproject") if err != nil { t.Error("GetWeeklyReportsUser failed:", err) } @@ -962,5 +962,6 @@ func TestRemoveProject(t *testing.T) { if len(projects) != 0 { t.Error("RemoveProject failed: expected 0, got", len(projects)) } - + } + \ No newline at end of file diff --git a/backend/internal/database/sample_data/0010_sample_data.sql b/backend/internal/database/sample_data/0010_sample_data.sql index 70499b0..ab74f1a 100644 --- a/backend/internal/database/sample_data/0010_sample_data.sql +++ b/backend/internal/database/sample_data/0010_sample_data.sql @@ -21,12 +21,6 @@ VALUES ("projecttest3","test project3", 1); INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) VALUES (1,1,"project_manager"); -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (1,2,"project_manager"); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (1,3,"project_manager"); - INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) VALUES (2,1,"member"); diff --git a/backend/internal/handlers/projects/AddUserToProject.go b/backend/internal/handlers/projects/AddUserToProject.go index 3195314..702b7dd 100644 --- a/backend/internal/handlers/projects/AddUserToProject.go +++ b/backend/internal/handlers/projects/AddUserToProject.go @@ -10,33 +10,42 @@ import ( // AddUserToProjectHandler is a handler that adds a user to a project with a specified role func 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 { + log.Info("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) - pm_name := claims["name"].(string) + adminUsername := claims["name"].(string) + log.Info("Admin username from claims:", adminUsername) - project := c.Params("projectName") - username := c.Query("userName") - - // Check if the user is a project manager - isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) + isAdmin, err := db.GetDb(c).IsSiteAdmin(adminUsername) if err != nil { - log.Info("Error checking if user is project manager:", err) + log.Info("Error checking admin status:", err) return c.Status(500).SendString(err.Error()) } - if !isPM { - log.Info("User: ", pm_name, " is not a project manager in project: ", project) - return c.Status(403).SendString("User is not a project manager") + if !isAdmin { + log.Info("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 = db.GetDb(c).AddUserToProject(username, project, "member") + err = db.GetDb(c).AddUserToProject(requestData.Username, requestData.ProjectName, requestData.Role) if err != nil { log.Info("Error adding user to project:", err) return c.Status(500).SendString(err.Error()) } // Return success message - log.Info("User : ", username, " added to project: ", project) + log.Info("User added to project successfully:", requestData.Username) return c.SendStatus(fiber.StatusOK) } diff --git a/backend/internal/handlers/projects/PromoteToPm.go b/backend/internal/handlers/projects/PromoteToPm.go deleted file mode 100644 index ffe2215..0000000 --- a/backend/internal/handlers/projects/PromoteToPm.go +++ /dev/null @@ -1,51 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// @Summary Promote to project manager -// @Description Promote a user to project manager -// @Tags Auth -// @Security JWT -// @Accept plain -// @Produce plain -// @Param projectName path string true "Project name" -// @Param userName query string true "User name" -// @Failure 500 {string} string "Internal server error" -// @Failure 403 {string} string "Forbidden" -// @Router /promote/{projectName} [put] -// -// Login logs in a user and returns a JWT token -// Promote to project manager -func PromoteToPm(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - pm_name := claims["name"].(string) - - project := c.Params("projectName") - new_pm_name := c.Query("userName") - - // Check if the user is a project manager - isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isPM { - log.Info("User: ", pm_name, " is not a project manager in project: ", project) - return c.Status(403).SendString("User is not a project manager") - } - - // Add the user to the project with the specified role - err = db.GetDb(c).ChangeUserRole(new_pm_name, project, "project_manager") - - // Return success message - log.Info("User : ", new_pm_name, " promoted to project manager in project: ", project) - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/projects/RemoveUserFromProject.go b/backend/internal/handlers/projects/RemoveUserFromProject.go deleted file mode 100644 index 7aefcf8..0000000 --- a/backend/internal/handlers/projects/RemoveUserFromProject.go +++ /dev/null @@ -1,40 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func RemoveUserFromProject(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - pm_name := claims["name"].(string) - - project := c.Params("projectName") - username := c.Query("userName") - - // Check if the user is a project manager - isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isPM { - log.Info("User: ", pm_name, " is not a project manager in project: ", project) - return c.Status(403).SendString("User is not a project manager") - } - - // Remove the user from the project - if err = db.GetDb(c).RemoveUserFromProject(username, project); err != nil { - log.Info("Error removing user from project:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return success message - log.Info("User : ", username, " removed from project: ", project) - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/reports/GetAllWeeklyReports.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go deleted file mode 100644 index ee81c82..0000000 --- a/backend/internal/handlers/reports/GetAllWeeklyReports.go +++ /dev/null @@ -1,56 +0,0 @@ -package reports - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// GetAllWeeklyReports retrieves all weekly reports for a user in a specific project -func GetAllWeeklyReports(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract project name and week from query parameters - projectName := c.Params("projectName") - target_user := c.Query("targetUser") // The user whose reports are being requested - - // If the target user is not empty, use it as the username - if target_user == "" { - target_user = username - } - - log.Info(username, " trying to get all weekly reports for: ", target_user) - - if projectName == "" { - log.Info("Missing project name") - return c.Status(400).SendString("Missing project name") - } - - // If the user is not a project manager, they can only view their own reports - pm, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if pm == false && target_user != username { - log.Info("Unauthorized access") - return c.Status(403).SendString("Unauthorized access") - } - - // Retrieve weekly reports for the user in the project from the database - reports, err := db.GetDb(c).GetAllWeeklyReports(target_user, projectName) - if err != nil { - log.Error("Error getting weekly reports for user:", target_user, "in project:", projectName, ":", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning weekly report") - // Return the retrieved weekly report - return c.JSON(reports) -} diff --git a/backend/internal/handlers/reports/GetWeeklyReport.go b/backend/internal/handlers/reports/GetWeeklyReport.go index 04bdc0d..422bc0b 100644 --- a/backend/internal/handlers/reports/GetWeeklyReport.go +++ b/backend/internal/handlers/reports/GetWeeklyReport.go @@ -16,17 +16,11 @@ func GetWeeklyReport(c *fiber.Ctx) error { claims := user.Claims.(jwt.MapClaims) username := claims["name"].(string) + log.Info("Getting weekly report for: ", username) + // Extract project name and week from query parameters projectName := c.Query("projectName") week := c.Query("week") - target_user := c.Query("targetUser") // The user whose report is being requested - - // If the target user is not empty, use it as the username - if target_user == "" { - target_user = username - } - - log.Info(username, " trying to get weekly report for: ", target_user) if projectName == "" || week == "" { log.Info("Missing project name or week number") @@ -40,20 +34,8 @@ func GetWeeklyReport(c *fiber.Ctx) error { return c.Status(400).SendString("Invalid week number") } - // If the token user is not an admin, check if the target user is the same as the token user - pm, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if pm == false && target_user != username { - log.Info("Unauthorized access") - return c.Status(403).SendString("Unauthorized access") - } - // Call the database function to get the weekly report - report, err := db.GetDb(c).GetWeeklyReport(target_user, projectName, weekInt) + report, err := db.GetDb(c).GetWeeklyReport(username, projectName, weekInt) if err != nil { log.Info("Error getting weekly report from db:", err) return c.Status(500).SendString(err.Error()) diff --git a/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go b/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go new file mode 100644 index 0000000..da8a90b --- /dev/null +++ b/backend/internal/handlers/reports/GetWeeklyReportsUserHandler.go @@ -0,0 +1,36 @@ +package reports + +import ( + db "ttime/internal/database" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" + "github.com/golang-jwt/jwt/v5" +) + +// GetWeeklyReportsUserHandler retrieves all weekly reports for a user in a specific project +func GetWeeklyReportsUserHandler(c *fiber.Ctx) error { + // Extract the necessary parameters from the token + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + username := claims["name"].(string) + + // Extract necessary (path) parameters from the request + projectName := c.Params("projectName") + + // TODO: Here we need to check whether the user is a member of the project + // If not, we should return an error. On the other hand, if the user not a member, + // the returned list of reports will (should) allways be empty. + + // Retrieve weekly reports for the user in the project from the database + reports, err := db.GetDb(c).GetWeeklyReportsUser(username, projectName) + if err != nil { + log.Error("Error getting weekly reports for user:", username, "in project:", projectName, ":", err) + return c.Status(500).SendString(err.Error()) + } + + log.Info("Returning weekly reports for user:", username, "in project:", projectName) + + // Return the list of reports as JSON + return c.JSON(reports) +} diff --git a/backend/internal/handlers/users/GetUserName.go b/backend/internal/handlers/users/GetUserName.go deleted file mode 100644 index 82b6cc8..0000000 --- a/backend/internal/handlers/users/GetUserName.go +++ /dev/null @@ -1,32 +0,0 @@ -package users - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" -) - -// Return the username of a user given their user id -func GetUserName(c *fiber.Ctx) error { - // Check the query params for userId - user_id_string := c.Query("userId") - if user_id_string == "" { - return c.Status(400).SendString("Missing user id") - } - - // Convert to int - user_id, err := strconv.Atoi(user_id_string) - if err != nil { - return c.Status(400).SendString("Invalid user id") - } - - // Get the username from the database - username, err := db.GetDb(c).GetUserName(user_id) - if err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Send the nuclear launch codes to north korea - return c.JSON(fiber.Map{"username": username}) -} diff --git a/backend/main.go b/backend/main.go index 7b19dd9..6e65386 100644 --- a/backend/main.go +++ b/backend/main.go @@ -103,7 +103,6 @@ func main() { // userGroup := api.Group("/user") // Not currently in use api.Get("/users/all", users.ListAllUsers) api.Get("/project/getAllUsers", users.GetAllUsersProject) - api.Get("/username", users.GetUserName) api.Post("/login", users.Login) api.Post("/register", users.Register) api.Post("/loginrenew", users.LoginRenew) @@ -120,9 +119,6 @@ func main() { api.Get("/getUsersProject/:projectName", projects.ListAllUsersProject) api.Post("/project", projects.CreateProject) api.Post("/ProjectRoleChange", projects.ProjectRoleChange) - api.Put("/promoteToPm/:projectName", projects.PromoteToPm) - api.Put("/addUserToProject/:projectName", projects.AddUserToProjectHandler) - api.Delete("/removeUserFromProject/:projectName", projects.RemoveUserFromProject) api.Delete("/removeProject/:projectName", projects.RemoveProject) api.Delete("/project/:projectID", projects.DeleteProject) @@ -130,9 +126,10 @@ func main() { // reportGroup := api.Group("/report") // Not currently in use api.Get("/getWeeklyReport", reports.GetWeeklyReport) api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports) - api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports) + api.Get("/getWeeklyReportsUser/:projectName", reports.GetWeeklyReportsUserHandler) api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport) api.Put("/signReport/:reportId", reports.SignReport) + api.Put("/addUserToProject", projects.AddUserToProjectHandler) api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport) // 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 86ad6dc..c1480fb 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -1,4 +1,4 @@ -import { AddMemberInfo } from "../Components/AddMember"; +import { NewProjMember } from "../Components/AddMember"; import { ProjectRoleChange } from "../Components/ChangeRole"; import { projectTimes } from "../Components/GetProjectTimes"; import { ProjectMember } from "../Components/GetUsersInProject"; @@ -99,20 +99,16 @@ interface API { token: string, ): Promise>; - /** Gets a weekly report for a specific user, project and week. - * Keep in mind that the user within the token needs to be PM - * of the project to get the report, unless the user is the target user. + /** Gets a weekly report for a specific user, project and week * @param {string} projectName The name of the project. * @param {string} week The week number. * @param {string} token The authentication token. - * @param {string} targetUser The username of the target user. Defaults to token user. * @returns {Promise>} A promise resolving to an API response with the retrieved report. */ getWeeklyReport( projectName: string, week: string, token: string, - targetUser?: string, ): Promise>; /** @@ -122,10 +118,9 @@ interface API { * @param {string} token The token of the user * @returns {APIResponse} A list of weekly reports */ - getAllWeeklyReportsForUser( + getWeeklyReportsForUser( projectName: string, token: string, - targetUser?: string, ): Promise>; /** Gets all the projects of a user @@ -197,13 +192,7 @@ interface API { ): Promise>; addUserToProject( - addMemberInfo: AddMemberInfo, - token: string, - ): Promise>; - - removeUserFromProject( - user: string, - project: string, + user: NewProjMember, token: string, ): Promise>; @@ -220,25 +209,6 @@ interface API { * @param {string} token The authentication token */ signReport(reportId: number, token: string): Promise>; - - /** - * Promotes a user to project manager within a project. - * - * @param {string} userName The username of the user to promote - * @param {string} projectName The name of the project to promote the user in - * @returns {Promise} A promise resolving to an API response. - */ - promoteToPm( - userName: string, - projectName: string, - token: string, - ): Promise>; - /** - * Get the username from the id - * @param {number} id The id of the user - * @param {string} token Your token - */ - getUsername(id: number, token: string): Promise>; } /** An instance of the API */ @@ -348,20 +318,18 @@ export const api: API = { }, async addUserToProject( - addMemberInfo: AddMemberInfo, + user: NewProjMember, token: string, ): Promise> { try { - const response = await fetch( - `/api/addUserToProject/${addMemberInfo.projectName}/?userName=${addMemberInfo.userName}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, + const response = await fetch("/api/addUserToProject", { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, }, - ); + body: JSON.stringify(user), + }); if (!response.ok) { return { success: false, message: "Failed to add member" }; @@ -373,31 +341,6 @@ export const api: API = { } }, - async removeUserFromProject( - user: string, - project: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/removeUserFromProject/${project}?userName=${user}`, - { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - if (!response.ok) { - return { success: false, message: "Failed to remove member" }; - } - } catch (e) { - return { success: false, message: "Failed to remove member" }; - } - return { success: true, message: "Removed member" }; - }, - async renewToken(token: string): Promise> { try { const response = await fetch("/api/loginrenew", { @@ -573,11 +516,10 @@ export const api: API = { projectName: string, week: string, token: string, - targetUser?: string, ): Promise> { try { const response = await fetch( - `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser ?? ""}`, + `/api/getWeeklyReport?projectName=${projectName}&week=${week}`, { method: "GET", headers: { @@ -598,22 +540,18 @@ export const api: API = { } }, - async getAllWeeklyReportsForUser( + async getWeeklyReportsForUser( projectName: string, token: string, - targetUser?: string, ): Promise> { try { - const response = await fetch( - `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser ?? ""}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, + const response = await fetch(`/api/getWeeklyReportsUser/${projectName}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, }, - ); + }); if (!response.ok) { return { @@ -845,56 +783,4 @@ export const api: API = { return { success: false, message: "Failed to sign report" }; } }, - - async promoteToPm( - userName: string, - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/promoteToPm/${projectName}?userName=${userName}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - if (!response.ok) { - return { - success: false, - message: "Failed to promote user to project manager", - }; - } - } catch (e) { - return { - success: false, - message: "Failed to promote user to project manager", - }; - } - return { success: true, message: "User promoted to project manager" }; - }, - - async getUsername(id: number, token: string): Promise> { - try { - const response = await fetch(`/api/username?userId=${id}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to get username" }; - } else { - const data = (await response.json()) as string; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to get username" }; - } - }, }; diff --git a/frontend/src/Components/AddMember.tsx b/frontend/src/Components/AddMember.tsx index d8036b7..194afe8 100644 --- a/frontend/src/Components/AddMember.tsx +++ b/frontend/src/Components/AddMember.tsx @@ -1,35 +1,44 @@ -import { api } from "../API/API"; +import { APIResponse, api } from "../API/API"; -export interface AddMemberInfo { - userName: string; - projectName: string; +export interface NewProjMember { + username: string; + role: string; + projectname: string; } /** * Tries to add a member to a project - * @param {AddMemberInfo} props.membertoAdd - Contains user's name and project's name - * @returns {Promise} + * @param {Object} props - A NewProjMember + * @returns {boolean} True if added, false if not */ -async function AddMember(props: { memberToAdd: AddMemberInfo }): Promise { - if (props.memberToAdd.userName === "") { - alert("You must choose at least one user to add"); - return; +function AddMember(props: { memberToAdd: NewProjMember }): boolean { + let added = false; + if ( + props.memberToAdd.username === "" || + props.memberToAdd.role === "" || + props.memberToAdd.projectname === "" + ) { + alert("All fields must be filled before adding"); + return added; } - try { - const response = await api.addUserToProject( + api + .addUserToProject( props.memberToAdd, localStorage.getItem("accessToken") ?? "", - ); - if (response.success) { - alert(`[${props.memberToAdd.userName}] added`); - } else { - alert(`[${props.memberToAdd.userName}] not added`); - console.error(response.message); - } - } catch (error) { - alert(`[${props.memberToAdd.userName}] not added`); - console.error("An error occurred during member add:", error); - } + ) + .then((response: APIResponse) => { + if (response.success) { + alert("Member added"); + added = true; + } else { + alert("Member not added"); + console.error(response.message); + } + }) + .catch((error) => { + console.error("An error occurred during member add:", error); + }); + return added; } export default AddMember; diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx index c8a1c66..e2ad8b9 100644 --- a/frontend/src/Components/AddProject.tsx +++ b/frontend/src/Components/AddProject.tsx @@ -1,10 +1,37 @@ import { useState } from "react"; -import { api } from "../API/API"; +import { APIResponse, api } from "../API/API"; import { NewProject } from "../Types/goTypes"; import InputField from "./InputField"; import Logo from "../assets/Logo.svg"; import Button from "./Button"; +/** + * Tries to add a project to the system + * @param {Object} props - Project name and description + * @returns {boolean} True if created, false if not + */ +function CreateProject(props: { name: string; description: string }): void { + const project: NewProject = { + name: props.name, + description: props.description, + }; + + api + .createProject(project, localStorage.getItem("accessToken") ?? "") + .then((response: APIResponse) => { + if (response.success) { + alert("Project added!"); + } else { + alert("Project NOT added!"); + console.error(response.message); + } + }) + .catch((error) => { + alert("Project NOT added!"); + console.error("An error occurred during creation:", error); + }); +} + /** * Provides UI for adding a project to the system. * @returns {JSX.Element} - Returns the component UI for adding a project @@ -13,33 +40,6 @@ function AddProject(): JSX.Element { const [name, setName] = useState(""); const [description, setDescription] = useState(""); - /** - * Tries to add a project to the system - */ - const handleCreateProject = async (): Promise => { - const project: NewProject = { - name: name.replace(/ /g, ""), - description: description.trim(), - }; - try { - const response = await api.createProject( - project, - localStorage.getItem("accessToken") ?? "", - ); - if (response.success) { - alert(`${project.name} added!`); - setDescription(""); - setName(""); - } else { - alert("Project not added, name could be taken"); - console.error(response.message); - } - } catch (error) { - alert("Project not added"); - console.error(error); - } - }; - return (
@@ -47,7 +47,10 @@ function AddProject(): JSX.Element { className="bg-white rounded px-8 pt-6 pb-8 mb-4 items-center justify-center flex flex-col w-fit h-fit" onSubmit={(e) => { e.preventDefault(); - void handleCreateProject(); + CreateProject({ + name: name, + description: description, + }); }} > ([]); + const [name, setName] = useState(""); const [users, setUsers] = useState([]); - const [usersProj, setUsersProj] = useState([]); - - // Gets all users and project members for filtering + const [role, setRole] = useState(""); GetAllUsers({ setUsersProp: setUsers }); - GetUsersInProject({ - setUsersProp: setUsersProj, - projectName: props.projectName, - }); - /* - * Filters the members from users so that users who are already - * members are not shown - */ - useEffect(() => { - setUsers((prevUsers) => { - const filteredUsers = prevUsers.filter( - (user) => - !usersProj.some((projectUser) => projectUser.Username === user), - ); - return filteredUsers; - }); - }, [usersProj]); - // Attempts to add all of the selected users to the project - const handleAddClick = async (): Promise => { - if (names.length === 0) - alert("You have to choose at least one user to add"); - for (const name of names) { - const newMember: AddMemberInfo = { - userName: name, - projectName: props.projectName, - }; - await AddMember({ memberToAdd: newMember }); - } - setNames([]); - location.reload(); - }; - - // Updates the names that have been selected - const handleUserClick = (user: string): void => { - setNames((prevNames): string[] => { - if (!prevNames.includes(user)) { - return [...prevNames, user]; - } - return prevNames.filter((name) => name !== user); - }); + const handleClick = (): boolean => { + const newMember: NewProjMember = { + username: name, + projectname: props.projectName, + role: role, + }; + return AddMember({ memberToAdd: newMember }); }; return ( -
-

- {props.projectName} -

-

- Choose users to add: +

+

+ User chosen: [{name}]

-
+

+ Role chosen: [{role}] +

+

+ Project chosen: [{props.projectName}] +

+

Choose role:

+
+
    +
  • { + setRole("member"); + }} + > + {"Member"} +
  • +
  • { + setRole("project_manager"); + }} + > + {"Project manager"} +
  • +
+
+

Choose user:

+
    {users.map((user) => (
  • { - handleUserClick(user); + setName(user); }} > {user} @@ -88,16 +73,13 @@ function AddUserToProject(props: { projectName: string }): JSX.Element { ))}
-

- Number of users to be added: {names.length} -

-
+
diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx index 0d5916b..4fa9ad8 100644 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ b/frontend/src/Components/AllTimeReportsInProject.tsx @@ -17,7 +17,7 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllWeeklyReportsForUser( + const response = await api.getWeeklyReportsForUser( projectName ?? "", token, ); diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx index cde9fa7..ef78642 100644 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx @@ -17,10 +17,10 @@ function AllTimeReportsInProject(): JSX.Element { useEffect(() => { const getWeeklyReports = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllWeeklyReportsForUser( + const response = await api.getWeeklyReportsForDifferentUser( projectName ?? "", - token, username ?? "", + token, ); console.log(response); if (response.success) { @@ -31,7 +31,7 @@ function AllTimeReportsInProject(): JSX.Element { }; void getWeeklyReports(); - }, [projectName, username]); + }, []); return ( <> diff --git a/frontend/src/Components/ChangeRoleView.tsx b/frontend/src/Components/ChangeRoleView.tsx index 782ad8d..30dce3c 100644 --- a/frontend/src/Components/ChangeRoleView.tsx +++ b/frontend/src/Components/ChangeRoleView.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import Button from "./Button"; import ChangeRole, { ProjectRoleChange } from "./ChangeRole"; -export default function ChangeRoleView(props: { +export default function ChangeRoles(props: { projectName: string; username: string; }): JSX.Element { diff --git a/frontend/src/Components/ChangeUsername.tsx b/frontend/src/Components/ChangeUsername.tsx index 2f73bb6..78d7da9 100644 --- a/frontend/src/Components/ChangeUsername.tsx +++ b/frontend/src/Components/ChangeUsername.tsx @@ -2,11 +2,8 @@ import { APIResponse, api } from "../API/API"; import { StrNameChange } from "../Types/goTypes"; function ChangeUsername(props: { nameChange: StrNameChange }): void { - if ( - props.nameChange.newName === "" || - props.nameChange.newName === props.nameChange.prevName - ) { - alert("You have to give a new name\n\nName not changed"); + if (props.nameChange.newName === "") { + alert("You have to select a new name"); return; } api @@ -16,7 +13,7 @@ function ChangeUsername(props: { nameChange: StrNameChange }): void { alert("Name changed successfully"); location.reload(); } else { - alert("Name not changed, name could be taken"); + alert("Name not changed"); console.error(response.message); } }) diff --git a/frontend/src/Components/DisplayUnsignedReports.tsx b/frontend/src/Components/DisplayUnsignedReports.tsx index 25a1da3..232cb31 100644 --- a/frontend/src/Components/DisplayUnsignedReports.tsx +++ b/frontend/src/Components/DisplayUnsignedReports.tsx @@ -3,14 +3,17 @@ import { Link, useParams } from "react-router-dom"; import { api } from "../API/API"; import { WeeklyReport } from "../Types/goTypes"; +/** + * Renders a component that displays the projects a user is a part of and links to the projects start-page. + * @returns The JSX element representing the component. + */ function DisplayUserProject(): JSX.Element { const { projectName } = useParams(); const [unsignedReports, setUnsignedReports] = useState([]); - const [usernames, setUsernames] = useState([]); - const token = localStorage.getItem("accessToken") ?? ""; - + //const navigate = useNavigate(); useEffect(() => { const getUnsignedReports = async (): Promise => { + const token = localStorage.getItem("accessToken") ?? ""; const response = await api.getUnsignedReportsInProject( projectName ?? "", token, @@ -18,21 +21,13 @@ function DisplayUserProject(): JSX.Element { console.log(response); if (response.success) { setUnsignedReports(response.data ?? []); - const usernamesPromises = (response.data ?? []).map((report) => - api.getUsername(report.userId, token), - ); - const usernamesResponses = await Promise.all(usernamesPromises); - const usernames = usernamesResponses.map( - (res) => (res.data as { username?: string }).username ?? "", - ); - setUsernames(usernames); } else { console.error(response.message); } }; void getUnsignedReports(); - }, [projectName, token]); + }, [projectName]); // Include 'projectName' in the dependency array return ( <> @@ -44,8 +39,8 @@ function DisplayUserProject(): JSX.Element {

- Username: -

{usernames[index]}

{" "} + UserID: +

{unsignedReport.userId}

Week:

{unsignedReport.week}

Total Time: @@ -63,7 +58,7 @@ function DisplayUserProject(): JSX.Element {

View Report diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx index 9be2f4b..eb4fa5a 100644 --- a/frontend/src/Components/Header.tsx +++ b/frontend/src/Components/Header.tsx @@ -1,6 +1,6 @@ //info: Header component to display the header of the page including the logo and user information where thr user can logout import { useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import backgroundImage from "../assets/1.jpg"; /** @@ -9,33 +9,23 @@ import backgroundImage from "../assets/1.jpg"; */ function Header(): JSX.Element { const [isOpen, setIsOpen] = useState(false); - const username = localStorage.getItem("username"); - const navigate = useNavigate(); const handleLogout = (): void => { localStorage.clear(); }; - const handleNavigation = (): void => { - if (username === "admin") { - navigate("/admin"); - } else { - navigate("/yourProjects"); - } - }; - return (
-
+ TTIME Logo -
+
@@ -42,16 +42,13 @@ function MemberInfoModal(props: {

Number of members: {users.length}

-

- Total time reported:{" "} - {Math.floor(totalTime.current / 60 / 24) + " d "} - {Math.floor((totalTime.current / 60) % 24) + " h "} - {(totalTime.current % 60) + " m "} -

+

Total time reported: {totalTime.current}

Project members:

diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx index be35a74..68e0979 100644 --- a/frontend/src/Components/Register.tsx +++ b/frontend/src/Components/Register.tsx @@ -15,21 +15,17 @@ export default function Register(): JSX.Element { const [errMessage, setErrMessage] = useState(); const handleRegister = async (): Promise => { - if (username === "" || password === "") { - alert("Must provide username and password"); - return; - } const newUser: NewUser = { - username: username?.replace(/ /g, "") ?? "", + username: username ?? "", password: password ?? "", }; const response = await api.registerUser(newUser); if (response.success) { - alert(`${newUser.username} added!`); + alert("User added!"); setPassword(""); setUsername(""); } else { - alert("User not added, name could be taken"); + alert("User not added"); setErrMessage(response.message ?? "Unknown error"); console.error(errMessage); } diff --git a/frontend/src/Components/RemoveUserFromProj.tsx b/frontend/src/Components/RemoveUserFromProj.tsx deleted file mode 100644 index eab79d9..0000000 --- a/frontend/src/Components/RemoveUserFromProj.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { api, APIResponse } from "../API/API"; - -/** - * Removes a user from a project - * @param {string} props.usernameToDelete - The username of user to remove - * @param {string} props.projectName - Project to remove user from - * @returns {void} - * @example - * const exampleUsername = "user"; - * const exampleProjectName "project"; - * RemoveUserFromProj({ userToRemove: exampleUsername, projectName: exampleProjectName }); - */ - -export default function RemoveUserFromProj(props: { - userToRemove: string; - projectName: string; -}): void { - if (props.userToRemove === localStorage.getItem("username")) { - alert("Cannot remove yourself"); - return; - } - api - .removeUserFromProject( - props.userToRemove, - props.projectName, - localStorage.getItem("accessToken") ?? "", - ) - .then((response: APIResponse) => { - if (response.success) { - alert(`${props.userToRemove} has been removed!`); - location.reload(); - } else { - alert(`${props.userToRemove} has not been removed due to an error`); - console.error(response.message); - } - }) - .catch((error) => { - alert(`${props.userToRemove} has not been removed due to an error`); - console.error("An error occurred during deletion:", error); - }); -} diff --git a/frontend/src/Components/TimePerActivity.tsx b/frontend/src/Components/TimePerActivity.tsx index c5e4a9f..6175845 100644 --- a/frontend/src/Components/TimePerActivity.tsx +++ b/frontend/src/Components/TimePerActivity.tsx @@ -8,12 +8,12 @@ import { projectTimes } from "./GetProjectTimes"; * @returns JSX.Element */ export default function TimePerRole(): JSX.Element { - const [development, setDevelopment] = useState(0); - const [meeting, setMeeting] = useState(0); - const [admin, setAdmin] = useState(0); - const [own_work, setOwnWork] = useState(0); - const [study, setStudy] = useState(0); - const [testing, setTesting] = useState(0); + const [development, setDevelopment] = useState(); + const [meeting, setMeeting] = useState(); + const [admin, setAdmin] = useState(); + const [own_work, setOwnWork] = useState(); + const [study, setStudy] = useState(); + const [testing, setTesting] = useState(); const token = localStorage.getItem("accessToken") ?? ""; const { projectName } = useParams(); diff --git a/frontend/src/Components/UserInfoModal.tsx b/frontend/src/Components/UserInfoModal.tsx index eae011c..8cb4c9d 100644 --- a/frontend/src/Components/UserInfoModal.tsx +++ b/frontend/src/Components/UserInfoModal.tsx @@ -28,7 +28,7 @@ function UserInfoModal(props: { const handleClickChangeName = (): void => { const nameChange: StrNameChange = { prevName: props.username, - newName: newUsername.replace(/ /g, ""), + newName: newUsername, }; ChangeUsername({ nameChange: nameChange }); }; diff --git a/frontend/src/Components/ViewOtherTimeReport.tsx b/frontend/src/Components/ViewOtherTimeReport.tsx index 3689854..bde0529 100644 --- a/frontend/src/Components/ViewOtherTimeReport.tsx +++ b/frontend/src/Components/ViewOtherTimeReport.tsx @@ -31,9 +31,8 @@ export default function GetOtherUsersReport(): JSX.Element { projectName ?? "", fetchedWeek?.toString() ?? "0", token, - username ?? "", ); - console.log(response); + if (response.success) { const report: WeeklyReport = response.data ?? { reportId: 0, @@ -63,33 +62,25 @@ export default function GetOtherUsersReport(): JSX.Element { void fetchUsersWeeklyReport(); }); - const handleSignWeeklyReport = async (): Promise => { - const response = await api.signReport(reportId, token); - if (response.success) { - return true; - } else { - return false; - } + const handleSignWeeklyReport = async (): Promise => { + await api.signReport(reportId, token); }; const navigate = useNavigate(); return ( <> -

{username}'s Report

+

+ {" "} + UserId: {username}'s Report +

{ e.preventDefault(); - void (async (): Promise => { - const success = await handleSignWeeklyReport(); - if (!success) { - alert("Failed to sign report!"); - return; - } - alert("Report successfully signed!"); - navigate(-1); - })(); + void handleSignWeeklyReport(); + alert("Report successfully signed!"); + navigate(-1); }} >
diff --git a/testing/testing.py b/testing.py similarity index 66% rename from testing/testing.py rename to testing.py index ba38ced..d4594d1 100644 --- a/testing/testing.py +++ b/testing.py @@ -1,23 +1,61 @@ import requests +import string +import random -# This modules contains helper functions for the tests -from helpers import * +debug_output = True + +def gprint(*args, **kwargs): + print("\033[92m", *args, "\033[00m", **kwargs) print("Running Tests...") +def dprint(*args, **kwargs): + if debug_output: + print(*args, **kwargs) + +def randomString(len=10): + """Generate a random string of fixed length""" + letters = string.ascii_lowercase + return "".join(random.choice(letters) for i in range(len)) + + # Defined once per test run username = "user_" + randomString() projectName = "project_" + randomString() +# The base URL of the API +base_url = "http://localhost:8080" -# ta bort auth i handlern för att få testet att gå igenom +# Endpoint to test +registerPath = base_url + "/api/register" +loginPath = base_url + "/api/login" +addProjectPath = base_url + "/api/project" +submitReportPath = base_url + "/api/submitWeeklyReport" +getWeeklyReportPath = base_url + "/api/getWeeklyReport" +getProjectPath = base_url + "/api/project" +signReportPath = base_url + "/api/signReport" +addUserToProjectPath = base_url + "/api/addUserToProject" +promoteToAdminPath = base_url + "/api/promoteToAdmin" +getUserProjectsPath = base_url + "/api/getUserProjects" +getWeeklyReportsUserPath = base_url + "/api/getWeeklyReportsUser" +checkIfProjectManagerPath = base_url + "/api/checkIfProjectManager" +ProjectRoleChangePath = base_url + "/api/ProjectRoleChange" +getUsersProjectPath = base_url + "/api/getUsersProject" +getUnsignedReportsPath = base_url + "/api/getUnsignedReports" +getChangeUserNamePath = base_url + "/api/changeUserName" +getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport" +removeProjectPath = base_url + "/api/removeProject" + +#ta bort auth i handlern för att få testet att gå igenom def test_ProjectRoleChange(): dprint("Testing ProjectRoleChange") localUsername = randomString() localProjectName = randomString() register(localUsername, "username_password") - token = login(localUsername, "username_password").json()["token"] + token = login(localUsername, "username_password").json()[ + "token" + ] # Just checking since this test is built somewhat differently than the others assert token != None, "Login failed" @@ -42,17 +80,16 @@ def test_ProjectRoleChange(): assert response.status_code == 200, "ProjectRoleChange failed" gprint("test_ProjectRoleChange successful") - + def test_get_user_projects(): - username = "user2" - password = "123" dprint("Testing get user projects") - loginResponse = login(username, password) + loginResponse = login("user2", "123") # Check if the user is added to the project response = requests.get( - getUserProjectsPath + "/" + username, + getUserProjectsPath, + json={"username": "user2"}, headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, ) dprint(response.text) @@ -61,6 +98,26 @@ def test_get_user_projects(): gprint("test_get_user_projects successful") +# Posts the username and password to the register endpoint +def register(username: string, password: string): + dprint("Registering with username: ", username, " and password: ", password) + response = requests.post( + registerPath, json={"username": username, "password": password} + ) + dprint(response.text) + return response + + +# Posts the username and password to the login endpoint +def login(username: string, password: string): + dprint("Logging in with username: ", username, " and password: ", password) + response = requests.post( + loginPath, json={"username": username, "password": password} + ) + dprint(response.text) + return response + + # Test function to login def test_login(): response = login(username, "always_same") @@ -76,7 +133,6 @@ def test_create_user(): assert response.status_code == 200, "Registration failed" gprint("test_create_user successful") - # Test function to add a project def test_add_project(): loginResponse = login(username, "always_same") @@ -90,7 +146,6 @@ def test_add_project(): assert response.status_code == 200, "Add project failed" gprint("test_add_project successful") - # Test function to submit a report def test_submit_report(): token = login(username, "always_same").json()["token"] @@ -112,7 +167,6 @@ def test_submit_report(): assert response.status_code == 200, "Submit report failed" gprint("test_submit_report successful") - # Test function to get a weekly report def test_get_weekly_report(): token = login(username, "always_same").json()["token"] @@ -140,58 +194,91 @@ def test_get_project(): # Test function to add a user to a project def test_add_user_to_project(): - # User to create - pm_user = "user" + randomString() - pm_passwd = "password" + # Log in as a site admin + admin_username = randomString() + admin_password = "admin_password" + dprint( + "Registering with username: ", admin_username, " and password: ", admin_password + ) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + dprint(response.text) - # User to add - member_user = "member" + randomString() - member_passwd = "password" + admin_token = login(admin_username, admin_password).json()["token"] + response = requests.post( + promoteToAdminPath, + json={"username": admin_username}, + headers={"Authorization": "Bearer " + admin_token}, + ) + dprint(response.text) + assert response.status_code == 200, "Promote to site admin failed" + dprint("Admin promoted to site admin successfully") - # Name of the project to be created - project_name = "project" + randomString() + # Create a new user to add to the project + new_user = randomString() + register(new_user, "new_user_password") - pm_token = register_and_login(pm_user, pm_passwd) - register(member_user, member_passwd) + # 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}, + ) - response = create_project(pm_token, project_name) - assert response.status_code == 200, "Create project failed" - - # Promote the user to project manager - response = addToProject(pm_token, member_user, project_name) + dprint(response.text) assert response.status_code == 200, "Add user to project failed" - + gprint("test_add_user_to_project successful") # Test function to sign a report def test_sign_report(): - # Pm user - pm_username = "pm" + randomString() - pm_password = "admin_password2" + # Create a project manager user + project_manager = randomString() + register(project_manager, "project_manager_password") - # User to add - member_user = "member" + randomString() - member_passwd = "password" + # Register an admin + admin_username = randomString() + admin_password = "admin_password2" + dprint( + "Registering with username: ", admin_username, " and password: ", admin_password + ) + response = requests.post( + registerPath, json={"username": admin_username, "password": admin_password} + ) + dprint(response.text) - # Name of the project to be created - project_name = "project" + randomString() + # 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}, + ) - # Register and get the tokens for both users - pm_token = register_and_login(pm_username, pm_password) - member_token = register_and_login(member_user, member_passwd) + 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" + dprint("Project manager added to project successfully") - # Create the project - response = create_project(pm_token, project_name) - assert response.status_code == 200, "Create project failed" - - # Add the user to the project - response = addToProject(pm_token, member_user, project_name) + # Log in as the project manager + project_manager_token = login(project_manager, "project_manager_password").json()[ + "token" + ] # Submit a report for the project - response = submitReport( - member_token, - { - "projectName": project_name, - "week": 1, + token = login(username, "always_same").json()["token"] + response = requests.post( + submitReportPath, + json={ + "projectName": projectName, + "week": 2, "developmentTime": 10, "meetingTime": 5, "adminTime": 5, @@ -199,40 +286,54 @@ def test_sign_report(): "studyTime": 10, "testingTime": 10, }, + headers={"Authorization": "Bearer " + token}, ) assert response.status_code == 200, "Submit report failed" + dprint("Submit report successful") # Retrieve the report ID - report_id = getReport(member_token, member_user, project_name)["reportId"] + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName, "week": 1}, + ) + dprint(response.text) + report_id = response.json()["reportId"] # Sign the report as the project manager - response = signReport(pm_token, report_id) + response = requests.put( + signReportPath + "/" + str(report_id), + headers={"Authorization": "Bearer " + project_manager_token}, + ) assert response.status_code == 200, "Sign report failed" dprint("Sign report successful") # Retrieve the report ID again for confirmation - report_id = getReport(member_token, member_user, project_name)["reportId"] - assert report_id != None, "Get report failed" + response = requests.get( + getWeeklyReportPath, + headers={"Authorization": "Bearer " + token}, + params={"username": username, "projectName": projectName, "week": 1}, + ) + dprint(response.text) gprint("test_sign_report successful") - # Test function to get weekly reports for a user in a project -def test_get_all_weekly_reports(): +def test_get_weekly_reports_user(): # Log in as the user token = login(username, "always_same").json()["token"] # Get weekly reports for the user in the project response = requests.get( - getAllWeeklyReportsPath + "/" + projectName, + getWeeklyReportsUserPath + "/" + projectName, headers={"Authorization": "Bearer " + token}, - params={"targetUser": username}, ) - + dprint(response.text) assert response.status_code == 200, "Get weekly reports for user failed" gprint("test_get_weekly_reports_user successful") + # Test function to check if a user is a project manager def test_check_if_project_manager(): # Log in as the user @@ -243,12 +344,11 @@ def test_check_if_project_manager(): checkIfProjectManagerPath + "/" + projectName, headers={"Authorization": "Bearer " + token}, ) - + dprint(response.text) assert response.status_code == 200, "Check if project manager failed" gprint("test_check_if_project_manager successful") - def test_ensure_manager_of_created_project(): # Create a new user to add to the project newUser = "karen_" + randomString() @@ -272,7 +372,6 @@ def test_ensure_manager_of_created_project(): assert response.json()["isProjectManager"] == True, "User is not project manager" gprint("test_ensure_admin_of_created_project successful") - def test_change_user_name(): # Register a new user new_user = randomString() @@ -292,7 +391,7 @@ def test_change_user_name(): ) admin_token = login(admin_username, admin_password).json()["token"] - # Promote to admin + # Promote to admin response = requests.post( promoteToAdminPath, json={"username": admin_username}, @@ -310,7 +409,6 @@ def test_change_user_name(): assert response.status_code == 200, "Change user name failed" gprint("test_change_user_name successful") - def test_list_all_users_project(): # Log in as a user who is a member of the project admin_username = randomString() @@ -334,12 +432,11 @@ def test_list_all_users_project(): # Make a request to list all users associated with the project response = requests.get( getUsersProjectPath + "/" + projectName, - headers={"Authorization": "Bearer " + admin_token}, + headers={"Authorization": "Bearer " + admin_token}, ) assert response.status_code == 200, "List all users project failed" gprint("test_list_all_users_project sucessful") - def test_update_weekly_report(): # Log in as the user token = login(username, "always_same").json()["token"] @@ -405,99 +502,20 @@ def test_remove_project(): assert response.status_code == 200, "Remove project failed" gprint("test_remove_project successful") - def test_get_unsigned_reports(): # Log in as the user - token = login("user2", "123").json()["token"] + token = login("user2", "123").json()["token"] - # Make a request to get all unsigned reports - response = requests.get( - getUnsignedReportsPath + "/" + projectName, - headers={"Authorization": "Bearer " + token}, - ) - assert response.status_code == 200, "Get unsigned reports failed" - gprint("test_get_unsigned_reports successful") - - -def test_get_other_users_report_as_pm(): - # Create user - user = randomString() - register(user, "password") - - # Create project - project = randomString() - pm_token = login(user, "password").json()["token"] - response = requests.post( - addProjectPath, - json={"name": project, "description": "This is a project"}, - headers={"Authorization": "Bearer " + pm_token}, - ) - assert response.status_code == 200, "Add project failed" - - # Create other user - other_user = randomString() - register(other_user, "password") - user_token = login(other_user, "password").json()["token"] - - # Add other user to project - response = requests.put( - addUserToProjectPath + "/" + project, - headers={"Authorization": "Bearer " + pm_token}, # note pm_token - params={"userName": other_user}, - ) - assert response.status_code == 200, "Add user to project failed" - - # Submit report as other user - response = requests.post( - submitReportPath, - json={ - "projectName": project, - "week": 1, - "developmentTime": 10, - "meetingTime": 5, - "adminTime": 5, - "ownWorkTime": 10, - "studyTime": 10, - "testingTime": 10, - }, - headers={"Authorization": "Bearer " + user_token}, - ) - assert response.status_code == 200, "Submit report failed" - - # Get report as project manager - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + pm_token}, - params={"targetUser": other_user, "projectName": project, "week": 1}, - ) - assert response.status_code == 200, "Get weekly report failed" - - -def test_promote_to_manager(): - # User to create - pm_user = "user" + randomString() - pm_passwd = "password" - - # User to promote - member_user = "member" + randomString() - member_passwd = "password" - - # Name of the project to be created - project_name = "project" + randomString() - - pm_token = register_and_login(pm_user, pm_passwd) - member_token = register_and_login(member_user, member_passwd) - - response = create_project(pm_token, project_name) - assert response.status_code == 200, "Create project failed" - - # Promote the user to project manager - response = promoteToManager(pm_token, member_user, project_name) - assert response.status_code == 200, "Promote to manager failed" + # Make a request to get all unsigned reports + response = requests.get( + getUnsignedReportsPath + "/" + projectName, + headers={"Authorization": "Bearer " + token}, + ) + assert response.status_code == 200, "Get unsigned reports failed" + gprint("test_get_unsigned_reports successful") if __name__ == "__main__": - test_promote_to_manager() test_remove_project() test_get_user_projects() test_create_user() @@ -508,7 +526,7 @@ if __name__ == "__main__": test_get_project() test_sign_report() test_add_user_to_project() - test_get_all_weekly_reports() + test_get_weekly_reports_user() test_check_if_project_manager() test_ProjectRoleChange() test_ensure_manager_of_created_project() @@ -516,4 +534,4 @@ if __name__ == "__main__": test_list_all_users_project() test_change_user_name() test_update_weekly_report() - test_get_other_users_report_as_pm() + \ No newline at end of file diff --git a/testing/helpers.py b/testing/helpers.py deleted file mode 100644 index 5f2f367..0000000 --- a/testing/helpers.py +++ /dev/null @@ -1,151 +0,0 @@ -import requests -import string -import random -import json - -# Helper function for the TTime API testing suite - -# For style guide, see: -# https://peps.python.org/pep-0008/#function-and-variable-names -# https://google.github.io/styleguide/pyguide.html#316-naming - -################## -## Static Paths ## -################## - -base_url = "http://localhost:8080" - -registerPath = base_url + "/api/register" -loginPath = base_url + "/api/login" -addProjectPath = base_url + "/api/project" -submitReportPath = base_url + "/api/submitWeeklyReport" -getWeeklyReportPath = base_url + "/api/getWeeklyReport" -getProjectPath = base_url + "/api/project" -signReportPath = base_url + "/api/signReport" -addUserToProjectPath = base_url + "/api/addUserToProject" -promoteToAdminPath = base_url + "/api/promoteToAdmin" -getUserProjectsPath = base_url + "/api/getUserProjects" -getAllWeeklyReportsPath = base_url + "/api/getAllWeeklyReports" -checkIfProjectManagerPath = base_url + "/api/checkIfProjectManager" -ProjectRoleChangePath = base_url + "/api/ProjectRoleChange" -getUsersProjectPath = base_url + "/api/getUsersProject" -getUnsignedReportsPath = base_url + "/api/getUnsignedReports" -getChangeUserNamePath = base_url + "/api/changeUserName" -getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport" -removeProjectPath = base_url + "/api/removeProject" -promoteToPmPath = base_url + "/api/promoteToPm" - -debug_output = True - - -def gprint(*args, **kwargs): - print("\033[92m", *args, "\033[00m", **kwargs) - - -def dprint(*args, **kwargs): - if debug_output: - print(*args, **kwargs) - - -def randomString(len=10): - """Generate a random string of fixed length""" - letters = string.ascii_lowercase - return "".join(random.choice(letters) for i in range(len)) - - -############ ############ ############ ############ ############ - - -# Posts the username and password to the register endpoint -def register(username: string, password: string): - dprint("Registering with username: ", username, " and password: ", password) - response = requests.post( - registerPath, json={"username": username, "password": password} - ) - dprint(response.text) - return response - - -# Posts the username and password to the login endpoint -def login(username: string, password: string): - dprint("Logging in with username: ", username, " and password: ", password) - response = requests.post( - loginPath, json={"username": username, "password": password} - ) - dprint(response.text) - return response - - -# Register a user and return the token -def register_and_login(username: string, password: string) -> string: - register(username, password) - response = login(username, password) - return response.json()["token"] - - -def create_project( - token: string, project_name: string, description: string = "Test description" -): - dprint("Creating project with name: ", project_name) - response = requests.post( - addProjectPath, - headers={"Authorization": "Bearer " + token}, - json={"name": project_name, "description": description}, - ) - dprint(response.text) - return response - - -# Add a user to a project, requires the user withing the token to be a project manager of said project -def addToProject(token: string, username: string, project_name: string): - dprint("Adding user with username: ", username, " to project: ", project_name) - response = requests.put( - addUserToProjectPath + "/" + project_name, - headers={"Authorization": "Bearer " + token}, - params={"userName": username}, - ) - dprint(response.text) - return response - - -def promoteToManager(token: string, username: string, project_name: string): - dprint( - "Promoting user with username: ", - username, - " to project manager of project: ", - project_name, - ) - response = requests.put( - promoteToPmPath + "/" + project_name, - headers={"Authorization": "Bearer " + token}, - params={"userName": username}, - ) - dprint(response.text) - return response - - -def submitReport(token: string, report): - dprint("Submitting report: ", report) - response = requests.post( - submitReportPath, - json=report, - headers={"Authorization": "Bearer " + token}, - ) - return response - - -def getReport(token: string, username: string, projectName: string): - # Retrieve the report ID - response = requests.get( - getWeeklyReportPath, - headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, - ) - return response.json() - - -def signReport(project_manager_token: string, report_id: int): - return requests.put( - signReportPath + "/" + str(report_id), - headers={"Authorization": "Bearer " + project_manager_token}, - )