From 2aade5d2fe6d694ca232ac676c9b857c86713136 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 18 Mar 2024 19:59:14 +0100 Subject: [PATCH 01/32] Docs example --- backend/docs/docs.go | 24 +++++++++++++++++++ .../handlers/handlers_user_related.go | 7 +++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 8026f58..75908b4 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -32,6 +32,17 @@ const docTemplate = `{ "User" ], "summary": "Register a new user", + "parameters": [ + { + "description": "User to register", + "name": "{string}", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.NewUser" + } + } + ], "responses": { "200": { "description": "User added", @@ -55,6 +66,19 @@ const docTemplate = `{ } } }, + "definitions": { + "types.NewUser": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + } + }, "externalDocs": { "description": "OpenAPI", "url": "https://swagger.io/resources/open-api/" diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 0f7c047..e454f9f 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -16,9 +16,10 @@ import ( // @Tags User // @Accept json // @Produce json -// @Success 200 {string} string "User added" -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" +// @Param {string} body types.NewUser true "User to register" +// @Success 200 {string} string "User added" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" // @Router /api/register [post] func (gs *GState) Register(c *fiber.Ctx) error { u := new(types.NewUser) From 2be4afd0e0892a35d4ec23e1f0aa2459fffe6418 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Mon, 18 Mar 2024 20:05:47 +0100 Subject: [PATCH 02/32] Correct ish swagger docstring --- backend/internal/handlers/handlers_user_related.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index e454f9f..9c98d4d 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -15,12 +15,12 @@ import ( // @Description Register a new user // @Tags User // @Accept json -// @Produce json -// @Param {string} body types.NewUser true "User to register" +// @Produce plain +// @Param NewUser body types.NewUser true "User to register" // @Success 200 {string} string "User added" // @Failure 400 {string} string "Bad request" // @Failure 500 {string} string "Internal server error" -// @Router /api/register [post] +// @Router /register [post] func (gs *GState) Register(c *fiber.Ctx) error { u := new(types.NewUser) if err := c.BodyParser(u); err != nil { From f3c5abf4f371ed4f763ccbe3b9684e60e2c69f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20H=C3=B6gbom=20Aronson?= Date: Mon, 18 Mar 2024 22:40:51 +0100 Subject: [PATCH 03/32] added docs for loginrenew, login, regsiter --- backend/docs/docs.go | 134 +++++++++++++++++- .../handlers/handlers_user_related.go | 40 +++++- backend/internal/types/users.go | 5 + 3 files changed, 173 insertions(+), 6 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 75908b4..ac1589c 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -19,14 +19,101 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/api/register": { + "/login": { + "post": { + "description": "logs the user in and returns a jwt token", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "login", + "parameters": [ + { + "description": "login info", + "name": "NewUser", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.NewUser" + } + } + ], + "responses": { + "200": { + "description": "Successfully signed token for user", + "schema": { + "type": "Token" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/loginerenew": { + "post": { + "description": "renews the users token", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "renews the users token", + "responses": { + "200": { + "description": "Successfully signed token for user", + "schema": { + "type": "Token" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/register": { "post": { "description": "Register a new user", "consumes": [ "application/json" ], "produces": [ - "application/json" + "text/plain" ], "tags": [ "User" @@ -35,7 +122,7 @@ const docTemplate = `{ "parameters": [ { "description": "User to register", - "name": "{string}", + "name": "NewUser", "in": "body", "required": true, "schema": { @@ -64,6 +151,47 @@ const docTemplate = `{ } } } + }, + "/userdelete/{username}": { + "delete": { + "description": "UserDelete deletes a user from the database", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "Deletes a user", + "responses": { + "200": { + "description": "User deleted", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "403": { + "description": "You can only delete yourself", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 9c98d4d..c88d3d3 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -16,7 +16,7 @@ import ( // @Tags User // @Accept json // @Produce plain -// @Param NewUser body types.NewUser true "User to register" +// @Param NewUser body types.NewUser true "User to register" // @Success 200 {string} string "User added" // @Failure 400 {string} string "Bad request" // @Failure 500 {string} string "Internal server error" @@ -39,6 +39,17 @@ func (gs *GState) Register(c *fiber.Ctx) error { // This path should obviously be protected in the future // UserDelete deletes a user from the database +// +// @Summary Deletes a user +// @Description UserDelete deletes a user from the database +// @Tags User +// @Accept json +// @Produce plain +// @Success 200 {string} string "User deleted" +// @Failure 403 {string} string "You can only delete yourself" +// @Failure 500 {string} string "Internal server error" +// @Failure 401 {string} string "Unauthorized" +// @Router /userdelete/{username} [delete] func (gs *GState) UserDelete(c *fiber.Ctx) error { // Read from path parameters username := c.Params("username") @@ -58,8 +69,21 @@ func (gs *GState) UserDelete(c *fiber.Ctx) error { } // Login is a simple login handler that returns a JWT token +// +// @Summary login +// @Description logs the user in and returns a jwt token +// @Tags User +// @Accept json +// @Param NewUser body types.NewUser true "login info" +// @Produce plain +// @Success 200 Token types.Token "Successfully signed token for user" +// @Failure 400 {string} string "Bad request" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /login [post] func (gs *GState) Login(c *fiber.Ctx) error { // The body type is identical to a NewUser + u := new(types.NewUser) if err := c.BodyParser(u); err != nil { println("Error parsing body") @@ -91,10 +115,20 @@ func (gs *GState) Login(c *fiber.Ctx) error { } println("Successfully signed token for user:", u.Username) - return c.JSON(fiber.Map{"token": t}) + return c.JSON(types.Token{Token: t}) } // LoginRenew is a simple handler that renews the token +// +// @Summary renews the users token +// @Description renews the users token +// @Tags User +// @Accept json +// @Produce plain +// @Success 200 Token types.Token "Successfully signed token for user" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /loginerenew [post] func (gs *GState) LoginRenew(c *fiber.Ctx) error { // For testing: curl localhost:3000/restricted -H "Authorization: Bearer " user := c.Locals("user").(*jwt.Token) @@ -110,7 +144,7 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } - return c.JSON(fiber.Map{"token": t}) + return c.JSON(types.Token{Token: t}) } // ListAllUsers is a handler that returns a list of all users in the application database diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go index e9dff67..d3f2170 100644 --- a/backend/internal/types/users.go +++ b/backend/internal/types/users.go @@ -27,3 +27,8 @@ type PublicUser struct { UserId string `json:"userId"` Username string `json:"username"` } + +// wrapper type for token +type Token struct { + Token string `json:"token"` +} From ad85194d4f5622edf7edeecad133b959c585d164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20H=C3=B6gbom=20Aronson?= Date: Mon, 18 Mar 2024 23:21:49 +0100 Subject: [PATCH 04/32] finished docs for user reletaded stucts and added endpoint for listallusers --- backend/docs/docs.go | 87 +++++++++++++++++++ .../handlers/handlers_user_related.go | 53 +++++++---- 2 files changed, 124 insertions(+), 16 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index ac1589c..1898804 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -106,6 +106,58 @@ const docTemplate = `{ } } }, + "/promoteToAdmin": { + "post": { + "description": "promote chosen user to admin", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "promote user to admin", + "parameters": [ + { + "description": "user info", + "name": "NewUser", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.NewUser" + } + } + ], + "responses": { + "200": { + "description": "Successfully prometed user", + "schema": { + "type": "json" + } + }, + "400": { + "description": "bad request", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, "/register": { "post": { "description": "Register a new user", @@ -192,6 +244,41 @@ const docTemplate = `{ } } } + }, + "/users/all": { + "get": { + "description": "lists all users", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "Lists users", + "responses": { + "200": { + "description": "Successfully signed token for user", + "schema": { + "type": "json" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index c88d3d3..f5a6f1b 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -16,10 +16,10 @@ import ( // @Tags User // @Accept json // @Produce plain -// @Param NewUser body types.NewUser true "User to register" -// @Success 200 {string} string "User added" -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" +// @Param NewUser body types.NewUser true "User to register" +// @Success 200 {string} string "User added" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" // @Router /register [post] func (gs *GState) Register(c *fiber.Ctx) error { u := new(types.NewUser) @@ -45,10 +45,10 @@ func (gs *GState) Register(c *fiber.Ctx) error { // @Tags User // @Accept json // @Produce plain -// @Success 200 {string} string "User deleted" -// @Failure 403 {string} string "You can only delete yourself" -// @Failure 500 {string} string "Internal server error" -// @Failure 401 {string} string "Unauthorized" +// @Success 200 {string} string "User deleted" +// @Failure 403 {string} string "You can only delete yourself" +// @Failure 500 {string} string "Internal server error" +// @Failure 401 {string} string "Unauthorized" // @Router /userdelete/{username} [delete] func (gs *GState) UserDelete(c *fiber.Ctx) error { // Read from path parameters @@ -74,12 +74,12 @@ func (gs *GState) UserDelete(c *fiber.Ctx) error { // @Description logs the user in and returns a jwt token // @Tags User // @Accept json -// @Param NewUser body types.NewUser true "login info" +// @Param NewUser body types.NewUser true "login info" // @Produce plain -// @Success 200 Token types.Token "Successfully signed token for user" -// @Failure 400 {string} string "Bad request" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" +// @Success 200 Token types.Token "Successfully signed token for user" +// @Failure 400 {string} string "Bad request" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" // @Router /login [post] func (gs *GState) Login(c *fiber.Ctx) error { // The body type is identical to a NewUser @@ -125,9 +125,9 @@ func (gs *GState) Login(c *fiber.Ctx) error { // @Tags User // @Accept json // @Produce plain -// @Success 200 Token types.Token "Successfully signed token for user" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" +// @Success 200 Token types.Token "Successfully signed token for user" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" // @Router /loginerenew [post] func (gs *GState) LoginRenew(c *fiber.Ctx) error { // For testing: curl localhost:3000/restricted -H "Authorization: Bearer " @@ -148,6 +148,16 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { } // ListAllUsers is a handler that returns a list of all users in the application database +// +// @Summary Lists users +// @Description lists all users +// @Tags User +// @Accept json +// @Produce plain +// @Success 200 {json} json "Successfully signed token for user" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /users/all [get] func (gs *GState) ListAllUsers(c *fiber.Ctx) error { // Get all users from the database users, err := gs.Db.GetAllUsersApplication() @@ -159,6 +169,17 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { return c.JSON(users) } +// @Summary promote user to admin +// @Description promote chosen user to admin +// @Tags User +// @Accept json +// @Produce plain +// @Param NewUser body types.NewUser true "user info" +// @Success 200 {json} json "Successfully prometed user" +// @Failure 400 {string} string "bad request" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /promoteToAdmin [post] func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { // Extract the username from the request body var newUser types.NewUser From ff9eba039f68f10e7de6f4f174eedf7dc1b6d159 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Mon, 18 Mar 2024 23:36:59 +0100 Subject: [PATCH 05/32] Minor fixes YourProjectsPage --- frontend/src/Pages/YourProjectsPage.tsx | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 973baa3..baf18ce 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -7,12 +7,12 @@ import BasicWindow from "../Components/BasicWindow"; export const ProjectNameContext = createContext(""); function UserProjectPage(): JSX.Element { - /* const [projects, setProjects] = useState([]); - */ const [selectedProject, setSelectedProject] = useState(""); + const [projects, setProjects] = useState([]); + const [selectedProject, setSelectedProject] = useState(""); - /* const getProjects = async (): Promise => { - const username = localStorage.getItem("username") ?? ""; // replace with actual username - const token = localStorage.getItem("accessToken") ?? ""; // replace with actual token + const getProjects = async (): Promise => { + const username = localStorage.getItem("username") ?? ""; + const token = localStorage.getItem("accessToken") ?? ""; const response = await api.getUserProjects(username, token); console.log(response); if (response.success) { @@ -24,15 +24,7 @@ function UserProjectPage(): JSX.Element { // Call getProjects when the component mounts useEffect(() => { getProjects(); - }, []); */ - - // Mock data - const projects: Project[] = [ - { id: "1", name: "Project Test App" }, - { id: "2", name: "Project 2" }, - { id: "3", name: "Project 3" }, - // Add more mock projects as needed - ]; + }, []); const handleProjectClick = (projectName: string): void => { setSelectedProject(projectName); From 8711f9a20d93a22ec1d24913b3c59f0b5e518b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20H=C3=B6gbom=20Aronson?= Date: Tue, 19 Mar 2024 00:27:31 +0100 Subject: [PATCH 06/32] tyding up and tryinng to get tokens in docs to work --- backend/docs/docs.go | 22 ++++++++++++++----- .../handlers/handlers_user_related.go | 11 +++++----- backend/main.go | 6 ++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 1898804..322c812 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -73,6 +73,11 @@ const docTemplate = `{ }, "/loginerenew": { "post": { + "security": [ + { + "bererToken": [] + } + ], "description": "renews the users token", "consumes": [ "application/json" @@ -83,7 +88,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "renews the users token", + "summary": "LoginRenews", "responses": { "200": { "description": "Successfully signed token for user", @@ -118,7 +123,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "promote user to admin", + "summary": "PromoteToAdmin", "parameters": [ { "description": "user info", @@ -170,7 +175,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "Register a new user", + "summary": "Register", "parameters": [ { "description": "User to register", @@ -216,7 +221,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "Deletes a user", + "summary": "UserDelete", "responses": { "200": { "description": "User deleted", @@ -257,7 +262,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "Lists users", + "summary": "ListsAllUsers", "responses": { "200": { "description": "Successfully signed token for user", @@ -294,6 +299,13 @@ const docTemplate = `{ } } }, + "securityDefinitions": { + "bererToken": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + }, "externalDocs": { "description": "OpenAPI", "url": "https://swagger.io/resources/open-api/" diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index f5a6f1b..943a977 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -11,7 +11,7 @@ import ( // Register is a simple handler that registers a new user // -// @Summary Register a new user +// @Summary Register // @Description Register a new user // @Tags User // @Accept json @@ -40,7 +40,7 @@ func (gs *GState) Register(c *fiber.Ctx) error { // This path should obviously be protected in the future // UserDelete deletes a user from the database // -// @Summary Deletes a user +// @Summary UserDelete // @Description UserDelete deletes a user from the database // @Tags User // @Accept json @@ -120,8 +120,9 @@ func (gs *GState) Login(c *fiber.Ctx) error { // LoginRenew is a simple handler that renews the token // -// @Summary renews the users token +// @Summary LoginRenews // @Description renews the users token +// @Security bererToken // @Tags User // @Accept json // @Produce plain @@ -149,7 +150,7 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { // ListAllUsers is a handler that returns a list of all users in the application database // -// @Summary Lists users +// @Summary ListsAllUsers // @Description lists all users // @Tags User // @Accept json @@ -169,7 +170,7 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { return c.JSON(users) } -// @Summary promote user to admin +// @Summary PromoteToAdmin // @Description promote chosen user to admin // @Tags User // @Accept json diff --git a/backend/main.go b/backend/main.go index 3e2fb75..c19533e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -22,6 +22,10 @@ import ( // @license.name AGPL // @license.url https://www.gnu.org/licenses/agpl-3.0.html +//@securityDefinitions.apikey bererToken +//@in header +//@name Authorization + // @host localhost:8080 // @BasePath /api @@ -83,7 +87,7 @@ func main() { server.Post("/api/signReport", gs.SignReport) server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) - + server.Get("/api/users/all", gs.ListAllUsers) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { From 7e4e35f597a54ccb4151e2574e347efdb6ef56e5 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 00:30:25 +0100 Subject: [PATCH 07/32] Silencing python testing, optional verbose output --- testing.py | 67 +++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/testing.py b/testing.py index 1eea03b..71b614e 100644 --- a/testing.py +++ b/testing.py @@ -2,6 +2,11 @@ import requests import string import random +debug_output = False + +def dprint(*args, **kwargs): + if debug_output: + print(*args, **kwargs) def randomString(len=10): """Generate a random string of fixed length""" @@ -31,7 +36,7 @@ getUserProjectsPath = base_url + "/api/getUserProjects" def test_get_user_projects(): - print("Testing get user projects") + dprint("Testing get user projects") loginResponse = login("user2", "123") # Check if the user is added to the project response = requests.get( @@ -39,29 +44,29 @@ def test_get_user_projects(): json={"username": "user2"}, headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, ) - print(response.text) - print(response.json()) + dprint(response.text) + dprint(response.json()) assert response.status_code == 200, "Get user projects failed" - print("got user projects successfully") + dprint("got user projects successfully") # Posts the username and password to the register endpoint def register(username: string, password: string): - print("Registering with username: ", username, " and password: ", password) + dprint("Registering with username: ", username, " and password: ", password) response = requests.post( registerPath, json={"username": username, "password": password} ) - print(response.text) + dprint(response.text) return response # Posts the username and password to the login endpoint def login(username: string, password: string): - print("Logging in with username: ", username, " and password: ", password) + dprint("Logging in with username: ", username, " and password: ", password) response = requests.post( loginPath, json={"username": username, "password": password} ) - print(response.text) + dprint(response.text) return response @@ -69,7 +74,7 @@ def login(username: string, password: string): def test_login(): response = login(username, "always_same") assert response.status_code == 200, "Login failed" - print("Login successful") + dprint("Login successful") return response.json()["token"] @@ -77,8 +82,7 @@ def test_login(): def test_create_user(): response = register(username, "always_same") assert response.status_code == 200, "Registration failed" - print("Registration successful") - + dprint("Registration successful") # Test function to add a project def test_add_project(): @@ -89,10 +93,9 @@ def test_add_project(): json={"name": projectName, "description": "This is a project"}, headers={"Authorization": "Bearer " + token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Add project failed" - print("Add project successful") - + dprint("Add project successful") # Test function to submit a report def test_submit_report(): @@ -111,10 +114,9 @@ def test_submit_report(): }, headers={"Authorization": "Bearer " + token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Submit report failed" - print("Submit report successful") - + dprint("Submit report successful") # Test function to get a weekly report def test_get_weekly_report(): @@ -124,7 +126,7 @@ def test_get_weekly_report(): headers={"Authorization": "Bearer " + token}, params={"username": username, "projectName": projectName, "week": 1}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Get weekly report failed" @@ -135,7 +137,7 @@ def test_get_project(): getProjectPath + "/1", # Assumes that the project with id 1 exists headers={"Authorization": "Bearer " + token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Get project failed" @@ -144,13 +146,13 @@ def test_add_user_to_project(): # Log in as a site admin admin_username = randomString() admin_password = "admin_password" - print( + dprint( "Registering with username: ", admin_username, " and password: ", admin_password ) response = requests.post( registerPath, json={"username": admin_username, "password": admin_password} ) - print(response.text) + dprint(response.text) admin_token = login(admin_username, admin_password).json()["token"] response = requests.post( @@ -158,9 +160,9 @@ def test_add_user_to_project(): json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Promote to site admin failed" - print("Admin promoted to site admin successfully") + dprint("Admin promoted to site admin successfully") # Create a new user to add to the project new_user = randomString() @@ -173,10 +175,9 @@ def test_add_user_to_project(): headers={"Authorization": "Bearer " + admin_token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Add user to project failed" - print("Add user to project successful") - + dprint("Add user to project successful") # Test function to sign a report def test_sign_report(): @@ -187,13 +188,13 @@ def test_sign_report(): # Register an admin admin_username = randomString() admin_password = "admin_password2" - print( + dprint( "Registering with username: ", admin_username, " and password: ", admin_password ) response = requests.post( registerPath, json={"username": admin_username, "password": admin_password} ) - print(response.text) + dprint(response.text) # Log in as the admin admin_token = login(admin_username, admin_password).json()["token"] @@ -213,7 +214,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + admin_token}, ) assert response.status_code == 200, "Add project manager to project failed" - print("Project manager added to project successfully") + dprint("Project manager added to project successfully") # Log in as the project manager project_manager_token = login(project_manager, "project_manager_password").json()[ @@ -237,7 +238,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + token}, ) assert response.status_code == 200, "Submit report failed" - print("Submit report successful") + dprint("Submit report successful") # Retrieve the report ID response = requests.get( @@ -245,7 +246,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + token}, params={"username": username, "projectName": projectName, "week": 1}, ) - print(response.text) + dprint(response.text) report_id = response.json()["reportId"] # Sign the report as the project manager @@ -255,7 +256,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + project_manager_token}, ) assert response.status_code == 200, "Sign report failed" - print("Sign report successful") + dprint("Sign report successful") # Retrieve the report ID again for confirmation response = requests.get( @@ -263,7 +264,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + token}, params={"username": username, "projectName": projectName, "week": 1}, ) - print(response.text) + dprint(response.text) if __name__ == "__main__": From 3e35586bbea83e73c83a61c1ff6a10a1014f16d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20H=C3=B6gbom=20Aronson?= Date: Mon, 18 Mar 2024 23:21:49 +0100 Subject: [PATCH 08/32] finished docs for user reletaded stucts and added endpoint for listallusers --- backend/docs/docs.go | 87 +++++++++++++++++++ .../handlers/handlers_user_related.go | 53 +++++++---- 2 files changed, 124 insertions(+), 16 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index ac1589c..1898804 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -106,6 +106,58 @@ const docTemplate = `{ } } }, + "/promoteToAdmin": { + "post": { + "description": "promote chosen user to admin", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "promote user to admin", + "parameters": [ + { + "description": "user info", + "name": "NewUser", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.NewUser" + } + } + ], + "responses": { + "200": { + "description": "Successfully prometed user", + "schema": { + "type": "json" + } + }, + "400": { + "description": "bad request", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, "/register": { "post": { "description": "Register a new user", @@ -192,6 +244,41 @@ const docTemplate = `{ } } } + }, + "/users/all": { + "get": { + "description": "lists all users", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "User" + ], + "summary": "Lists users", + "responses": { + "200": { + "description": "Successfully signed token for user", + "schema": { + "type": "json" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 1d94270..1ed6bd4 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -17,10 +17,10 @@ import ( // @Tags User // @Accept json // @Produce plain -// @Param NewUser body types.NewUser true "User to register" -// @Success 200 {string} string "User added" -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" +// @Param NewUser body types.NewUser true "User to register" +// @Success 200 {string} string "User added" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" // @Router /register [post] func (gs *GState) Register(c *fiber.Ctx) error { u := new(types.NewUser) @@ -47,10 +47,10 @@ func (gs *GState) Register(c *fiber.Ctx) error { // @Tags User // @Accept json // @Produce plain -// @Success 200 {string} string "User deleted" -// @Failure 403 {string} string "You can only delete yourself" -// @Failure 500 {string} string "Internal server error" -// @Failure 401 {string} string "Unauthorized" +// @Success 200 {string} string "User deleted" +// @Failure 403 {string} string "You can only delete yourself" +// @Failure 500 {string} string "Internal server error" +// @Failure 401 {string} string "Unauthorized" // @Router /userdelete/{username} [delete] func (gs *GState) UserDelete(c *fiber.Ctx) error { // Read from path parameters @@ -79,12 +79,12 @@ func (gs *GState) UserDelete(c *fiber.Ctx) error { // @Description logs the user in and returns a jwt token // @Tags User // @Accept json -// @Param NewUser body types.NewUser true "login info" +// @Param NewUser body types.NewUser true "login info" // @Produce plain -// @Success 200 Token types.Token "Successfully signed token for user" -// @Failure 400 {string} string "Bad request" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" +// @Success 200 Token types.Token "Successfully signed token for user" +// @Failure 400 {string} string "Bad request" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" // @Router /login [post] func (gs *GState) Login(c *fiber.Ctx) error { // The body type is identical to a NewUser @@ -130,9 +130,9 @@ func (gs *GState) Login(c *fiber.Ctx) error { // @Tags User // @Accept json // @Produce plain -// @Success 200 Token types.Token "Successfully signed token for user" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" +// @Success 200 Token types.Token "Successfully signed token for user" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" // @Router /loginerenew [post] func (gs *GState) LoginRenew(c *fiber.Ctx) error { user := c.Locals("user").(*jwt.Token) @@ -158,6 +158,16 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { } // ListAllUsers is a handler that returns a list of all users in the application database +// +// @Summary Lists users +// @Description lists all users +// @Tags User +// @Accept json +// @Produce plain +// @Success 200 {json} json "Successfully signed token for user" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /users/all [get] func (gs *GState) ListAllUsers(c *fiber.Ctx) error { // Get all users from the database users, err := gs.Db.GetAllUsersApplication() @@ -171,6 +181,17 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { return c.JSON(users) } +// @Summary promote user to admin +// @Description promote chosen user to admin +// @Tags User +// @Accept json +// @Produce plain +// @Param NewUser body types.NewUser true "user info" +// @Success 200 {json} json "Successfully prometed user" +// @Failure 400 {string} string "bad request" +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Router /promoteToAdmin [post] func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { // Extract the username from the request body var newUser types.NewUser From 9ce70e74e922279f4881407b49b096ba5a4681fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20H=C3=B6gbom=20Aronson?= Date: Tue, 19 Mar 2024 00:27:31 +0100 Subject: [PATCH 09/32] tyding up and tryinng to get tokens in docs to work --- backend/docs/docs.go | 22 ++++++++++++++----- .../handlers/handlers_user_related.go | 11 +++++----- backend/main.go | 6 ++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 1898804..322c812 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -73,6 +73,11 @@ const docTemplate = `{ }, "/loginerenew": { "post": { + "security": [ + { + "bererToken": [] + } + ], "description": "renews the users token", "consumes": [ "application/json" @@ -83,7 +88,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "renews the users token", + "summary": "LoginRenews", "responses": { "200": { "description": "Successfully signed token for user", @@ -118,7 +123,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "promote user to admin", + "summary": "PromoteToAdmin", "parameters": [ { "description": "user info", @@ -170,7 +175,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "Register a new user", + "summary": "Register", "parameters": [ { "description": "User to register", @@ -216,7 +221,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "Deletes a user", + "summary": "UserDelete", "responses": { "200": { "description": "User deleted", @@ -257,7 +262,7 @@ const docTemplate = `{ "tags": [ "User" ], - "summary": "Lists users", + "summary": "ListsAllUsers", "responses": { "200": { "description": "Successfully signed token for user", @@ -294,6 +299,13 @@ const docTemplate = `{ } } }, + "securityDefinitions": { + "bererToken": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + }, "externalDocs": { "description": "OpenAPI", "url": "https://swagger.io/resources/open-api/" diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 1ed6bd4..96fddb7 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -12,7 +12,7 @@ import ( // Register is a simple handler that registers a new user // -// @Summary Register a new user +// @Summary Register // @Description Register a new user // @Tags User // @Accept json @@ -42,7 +42,7 @@ func (gs *GState) Register(c *fiber.Ctx) error { // This path should obviously be protected in the future // UserDelete deletes a user from the database // -// @Summary Deletes a user +// @Summary UserDelete // @Description UserDelete deletes a user from the database // @Tags User // @Accept json @@ -125,8 +125,9 @@ func (gs *GState) Login(c *fiber.Ctx) error { // LoginRenew is a simple handler that renews the token // -// @Summary renews the users token +// @Summary LoginRenews // @Description renews the users token +// @Security bererToken // @Tags User // @Accept json // @Produce plain @@ -159,7 +160,7 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { // ListAllUsers is a handler that returns a list of all users in the application database // -// @Summary Lists users +// @Summary ListsAllUsers // @Description lists all users // @Tags User // @Accept json @@ -181,7 +182,7 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { return c.JSON(users) } -// @Summary promote user to admin +// @Summary PromoteToAdmin // @Description promote chosen user to admin // @Tags User // @Accept json diff --git a/backend/main.go b/backend/main.go index 9abe995..76c5f99 100644 --- a/backend/main.go +++ b/backend/main.go @@ -23,6 +23,10 @@ import ( // @license.name AGPL // @license.url https://www.gnu.org/licenses/agpl-3.0.html +//@securityDefinitions.apikey bererToken +//@in header +//@name Authorization + // @host localhost:8080 // @BasePath /api @@ -89,7 +93,7 @@ func main() { server.Post("/api/signReport", gs.SignReport) server.Put("/api/addUserToProject", gs.AddUserToProjectHandler) server.Post("/api/promoteToAdmin", gs.PromoteToAdmin) - + server.Get("/api/users/all", gs.ListAllUsers) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) if err != nil { From b8c69fabf5f35c1505725fff97d3b27513e17fbb Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 00:35:27 +0100 Subject: [PATCH 10/32] Remove unused variable and update API call in YourProjectsPage.tsx --- 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 baf18ce..b8034f3 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -11,9 +11,8 @@ function UserProjectPage(): JSX.Element { const [selectedProject, setSelectedProject] = useState(""); const getProjects = async (): Promise => { - const username = localStorage.getItem("username") ?? ""; const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getUserProjects(username, token); + const response = await api.getUserProjects(token); console.log(response); if (response.success) { setProjects(response.data ?? []); From b174ec8922aa7761ba5095f64ea2752aa3f10d06 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 00:41:30 +0100 Subject: [PATCH 11/32] Rename for clarity and consistensy with TS api --- backend/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/main.go b/backend/main.go index 76c5f99..e578c52 100644 --- a/backend/main.go +++ b/backend/main.go @@ -83,7 +83,7 @@ func main() { })) // Protected routes (require a valid JWT bearer token authentication header) - server.Post("/api/submitReport", gs.SubmitWeeklyReport) + server.Post("/api/submitWeeklyReport", gs.SubmitWeeklyReport) server.Get("/api/getUserProjects", gs.GetUserProjects) server.Post("/api/loginrenew", gs.LoginRenew) server.Delete("/api/userdelete/:username", gs.UserDelete) // Perhaps just use POST to avoid headaches From c03be8c5d91d8c1bb5b15a9292e7a6c575c2629f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 00:46:28 +0100 Subject: [PATCH 12/32] Path rename in python testing script --- testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing.py b/testing.py index 1eea03b..ffadb56 100644 --- a/testing.py +++ b/testing.py @@ -20,7 +20,7 @@ base_url = "http://localhost:8080" registerPath = base_url + "/api/register" loginPath = base_url + "/api/login" addProjectPath = base_url + "/api/project" -submitReportPath = base_url + "/api/submitReport" +submitReportPath = base_url + "/api/submitWeeklyReport" getWeeklyReportPath = base_url + "/api/getWeeklyReport" getProjectPath = base_url + "/api/project" signReportPath = base_url + "/api/signReport" From 0217f2b51259d6b1fd29e2cd8a65a0ffa6fd5bbf Mon Sep 17 00:00:00 2001 From: borean Date: Tue, 19 Mar 2024 01:10:02 +0100 Subject: [PATCH 13/32] AddProject changed so that user is promoted to projectmanager --- backend/internal/database/db.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 25dd04b..e95ae4c 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -201,8 +201,15 @@ func (d *Db) GetProjectId(projectname string) (int, error) { // Creates a new project in the database, associated with a user func (d *Db) AddProject(name string, description string, username string) error { - _, err := d.Exec(projectInsert, name, description, username) - return err + _, err1 := d.Exec(projectInsert, name, description, username) + + // Immediately promotes said user to project manager + err2 := d.ChangeUserRole(username, name, "project_manager") + if err2 != nil { + panic(err2) + } + + return err1 } func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) { From 5bcca0202b1c6ed1285ee86a6e1bc56c62fa9fc7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 01:12:14 +0100 Subject: [PATCH 14/32] Better test feedback in python script --- testing.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/testing.py b/testing.py index 71b614e..f4666a7 100644 --- a/testing.py +++ b/testing.py @@ -4,6 +4,11 @@ import random debug_output = False +def gprint(*args, **kwargs): + print("\033[92m", *args, "\033[00m", **kwargs) + +print("Running Tests...") + def dprint(*args, **kwargs): if debug_output: print(*args, **kwargs) @@ -47,7 +52,7 @@ def test_get_user_projects(): dprint(response.text) dprint(response.json()) assert response.status_code == 200, "Get user projects failed" - dprint("got user projects successfully") + gprint("test_get_user_projects successful") # Posts the username and password to the register endpoint @@ -75,6 +80,7 @@ def test_login(): response = login(username, "always_same") assert response.status_code == 200, "Login failed" dprint("Login successful") + gprint("test_login successful") return response.json()["token"] @@ -82,7 +88,7 @@ def test_login(): def test_create_user(): response = register(username, "always_same") assert response.status_code == 200, "Registration failed" - dprint("Registration successful") + gprint("test_create_user successful") # Test function to add a project def test_add_project(): @@ -95,7 +101,7 @@ def test_add_project(): ) dprint(response.text) assert response.status_code == 200, "Add project failed" - dprint("Add project successful") + gprint("test_add_project successful") # Test function to submit a report def test_submit_report(): @@ -116,7 +122,7 @@ def test_submit_report(): ) dprint(response.text) assert response.status_code == 200, "Submit report failed" - dprint("Submit report successful") + gprint("test_submit_report successful") # Test function to get a weekly report def test_get_weekly_report(): @@ -128,6 +134,7 @@ def test_get_weekly_report(): ) dprint(response.text) assert response.status_code == 200, "Get weekly report failed" + gprint("test_get_weekly_report successful") # Tests getting a project by id @@ -139,6 +146,7 @@ def test_get_project(): ) dprint(response.text) assert response.status_code == 200, "Get project failed" + gprint("test_get_project successful") # Test function to add a user to a project @@ -177,7 +185,7 @@ def test_add_user_to_project(): dprint(response.text) assert response.status_code == 200, "Add user to project failed" - dprint("Add user to project successful") + gprint("test_add_user_to_project successful") # Test function to sign a report def test_sign_report(): @@ -265,6 +273,7 @@ def test_sign_report(): params={"username": username, "projectName": projectName, "week": 1}, ) dprint(response.text) + gprint("test_sign_report successful") if __name__ == "__main__": From 58d9001be3e79275e14d8ab4e1fe2dabc453d67a Mon Sep 17 00:00:00 2001 From: borean Date: Tue, 19 Mar 2024 01:10:02 +0100 Subject: [PATCH 15/32] AddProject changed so that user is promoted to projectmanager --- backend/internal/database/db.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 25dd04b..e95ae4c 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -201,8 +201,15 @@ func (d *Db) GetProjectId(projectname string) (int, error) { // Creates a new project in the database, associated with a user func (d *Db) AddProject(name string, description string, username string) error { - _, err := d.Exec(projectInsert, name, description, username) - return err + _, err1 := d.Exec(projectInsert, name, description, username) + + // Immediately promotes said user to project manager + err2 := d.ChangeUserRole(username, name, "project_manager") + if err2 != nil { + panic(err2) + } + + return err1 } func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) { From e498f0ed6380c06d61018cfca42c1ba88f06d545 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 00:30:25 +0100 Subject: [PATCH 16/32] Silencing python testing, optional verbose output --- testing.py | 67 +++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/testing.py b/testing.py index ffadb56..f1e17c0 100644 --- a/testing.py +++ b/testing.py @@ -2,6 +2,11 @@ import requests import string import random +debug_output = False + +def dprint(*args, **kwargs): + if debug_output: + print(*args, **kwargs) def randomString(len=10): """Generate a random string of fixed length""" @@ -31,7 +36,7 @@ getUserProjectsPath = base_url + "/api/getUserProjects" def test_get_user_projects(): - print("Testing get user projects") + dprint("Testing get user projects") loginResponse = login("user2", "123") # Check if the user is added to the project response = requests.get( @@ -39,29 +44,29 @@ def test_get_user_projects(): json={"username": "user2"}, headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, ) - print(response.text) - print(response.json()) + dprint(response.text) + dprint(response.json()) assert response.status_code == 200, "Get user projects failed" - print("got user projects successfully") + dprint("got user projects successfully") # Posts the username and password to the register endpoint def register(username: string, password: string): - print("Registering with username: ", username, " and password: ", password) + dprint("Registering with username: ", username, " and password: ", password) response = requests.post( registerPath, json={"username": username, "password": password} ) - print(response.text) + dprint(response.text) return response # Posts the username and password to the login endpoint def login(username: string, password: string): - print("Logging in with username: ", username, " and password: ", password) + dprint("Logging in with username: ", username, " and password: ", password) response = requests.post( loginPath, json={"username": username, "password": password} ) - print(response.text) + dprint(response.text) return response @@ -69,7 +74,7 @@ def login(username: string, password: string): def test_login(): response = login(username, "always_same") assert response.status_code == 200, "Login failed" - print("Login successful") + dprint("Login successful") return response.json()["token"] @@ -77,8 +82,7 @@ def test_login(): def test_create_user(): response = register(username, "always_same") assert response.status_code == 200, "Registration failed" - print("Registration successful") - + dprint("Registration successful") # Test function to add a project def test_add_project(): @@ -89,10 +93,9 @@ def test_add_project(): json={"name": projectName, "description": "This is a project"}, headers={"Authorization": "Bearer " + token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Add project failed" - print("Add project successful") - + dprint("Add project successful") # Test function to submit a report def test_submit_report(): @@ -111,10 +114,9 @@ def test_submit_report(): }, headers={"Authorization": "Bearer " + token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Submit report failed" - print("Submit report successful") - + dprint("Submit report successful") # Test function to get a weekly report def test_get_weekly_report(): @@ -124,7 +126,7 @@ def test_get_weekly_report(): headers={"Authorization": "Bearer " + token}, params={"username": username, "projectName": projectName, "week": 1}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Get weekly report failed" @@ -135,7 +137,7 @@ def test_get_project(): getProjectPath + "/1", # Assumes that the project with id 1 exists headers={"Authorization": "Bearer " + token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Get project failed" @@ -144,13 +146,13 @@ def test_add_user_to_project(): # Log in as a site admin admin_username = randomString() admin_password = "admin_password" - print( + dprint( "Registering with username: ", admin_username, " and password: ", admin_password ) response = requests.post( registerPath, json={"username": admin_username, "password": admin_password} ) - print(response.text) + dprint(response.text) admin_token = login(admin_username, admin_password).json()["token"] response = requests.post( @@ -158,9 +160,9 @@ def test_add_user_to_project(): json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Promote to site admin failed" - print("Admin promoted to site admin successfully") + dprint("Admin promoted to site admin successfully") # Create a new user to add to the project new_user = randomString() @@ -173,10 +175,9 @@ def test_add_user_to_project(): headers={"Authorization": "Bearer " + admin_token}, ) - print(response.text) + dprint(response.text) assert response.status_code == 200, "Add user to project failed" - print("Add user to project successful") - + dprint("Add user to project successful") # Test function to sign a report def test_sign_report(): @@ -187,13 +188,13 @@ def test_sign_report(): # Register an admin admin_username = randomString() admin_password = "admin_password2" - print( + dprint( "Registering with username: ", admin_username, " and password: ", admin_password ) response = requests.post( registerPath, json={"username": admin_username, "password": admin_password} ) - print(response.text) + dprint(response.text) # Log in as the admin admin_token = login(admin_username, admin_password).json()["token"] @@ -213,7 +214,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + admin_token}, ) assert response.status_code == 200, "Add project manager to project failed" - print("Project manager added to project successfully") + dprint("Project manager added to project successfully") # Log in as the project manager project_manager_token = login(project_manager, "project_manager_password").json()[ @@ -237,7 +238,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + token}, ) assert response.status_code == 200, "Submit report failed" - print("Submit report successful") + dprint("Submit report successful") # Retrieve the report ID response = requests.get( @@ -245,7 +246,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + token}, params={"username": username, "projectName": projectName, "week": 1}, ) - print(response.text) + dprint(response.text) report_id = response.json()["reportId"] # Sign the report as the project manager @@ -255,7 +256,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + project_manager_token}, ) assert response.status_code == 200, "Sign report failed" - print("Sign report successful") + dprint("Sign report successful") # Retrieve the report ID again for confirmation response = requests.get( @@ -263,7 +264,7 @@ def test_sign_report(): headers={"Authorization": "Bearer " + token}, params={"username": username, "projectName": projectName, "week": 1}, ) - print(response.text) + dprint(response.text) if __name__ == "__main__": From 09014c66596d48c0c4f36574d007864e8f51da3e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 01:12:14 +0100 Subject: [PATCH 17/32] Better test feedback in python script --- testing.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/testing.py b/testing.py index f1e17c0..a3de715 100644 --- a/testing.py +++ b/testing.py @@ -4,6 +4,11 @@ import random debug_output = False +def gprint(*args, **kwargs): + print("\033[92m", *args, "\033[00m", **kwargs) + +print("Running Tests...") + def dprint(*args, **kwargs): if debug_output: print(*args, **kwargs) @@ -47,7 +52,7 @@ def test_get_user_projects(): dprint(response.text) dprint(response.json()) assert response.status_code == 200, "Get user projects failed" - dprint("got user projects successfully") + gprint("test_get_user_projects successful") # Posts the username and password to the register endpoint @@ -75,6 +80,7 @@ def test_login(): response = login(username, "always_same") assert response.status_code == 200, "Login failed" dprint("Login successful") + gprint("test_login successful") return response.json()["token"] @@ -82,7 +88,7 @@ def test_login(): def test_create_user(): response = register(username, "always_same") assert response.status_code == 200, "Registration failed" - dprint("Registration successful") + gprint("test_create_user successful") # Test function to add a project def test_add_project(): @@ -95,7 +101,7 @@ def test_add_project(): ) dprint(response.text) assert response.status_code == 200, "Add project failed" - dprint("Add project successful") + gprint("test_add_project successful") # Test function to submit a report def test_submit_report(): @@ -116,7 +122,7 @@ def test_submit_report(): ) dprint(response.text) assert response.status_code == 200, "Submit report failed" - dprint("Submit report successful") + gprint("test_submit_report successful") # Test function to get a weekly report def test_get_weekly_report(): @@ -128,6 +134,7 @@ def test_get_weekly_report(): ) dprint(response.text) assert response.status_code == 200, "Get weekly report failed" + gprint("test_get_weekly_report successful") # Tests getting a project by id @@ -139,6 +146,7 @@ def test_get_project(): ) dprint(response.text) assert response.status_code == 200, "Get project failed" + gprint("test_get_project successful") # Test function to add a user to a project @@ -177,7 +185,7 @@ def test_add_user_to_project(): dprint(response.text) assert response.status_code == 200, "Add user to project failed" - dprint("Add user to project successful") + gprint("test_add_user_to_project successful") # Test function to sign a report def test_sign_report(): @@ -265,6 +273,7 @@ def test_sign_report(): params={"username": username, "projectName": projectName, "week": 1}, ) dprint(response.text) + gprint("test_sign_report successful") if __name__ == "__main__": From 3125b511bbabaa79f23b1b3559b264c7a4bc880d Mon Sep 17 00:00:00 2001 From: borean Date: Tue, 19 Mar 2024 01:38:40 +0100 Subject: [PATCH 18/32] correcting AddProject --- backend/internal/database/db.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index e95ae4c..ad408a7 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -201,15 +201,19 @@ func (d *Db) GetProjectId(projectname string) (int, error) { // Creates a new project in the database, associated with a user func (d *Db) AddProject(name string, description string, username string) error { - _, err1 := d.Exec(projectInsert, name, description, username) - - // Immediately promotes said user to project manager - err2 := d.ChangeUserRole(username, name, "project_manager") - if err2 != nil { - panic(err2) + tx := d.MustBegin() + _, err := tx.Exec(projectInsert, name, description, username) + if err != nil { + tx.Rollback() + return err } - - return err1 + _, err = tx.Exec(changeUserRole, "project_manager", username, name) + if err != nil { + tx.Rollback() + return err + } + tx.Commit() + return err } func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) { From 8081f289b5a399e2481919bdee4fe25ca866005a Mon Sep 17 00:00:00 2001 From: al8763be Date: Tue, 19 Mar 2024 02:11:47 +0100 Subject: [PATCH 19/32] fixed NewWeeklyReport --- frontend/src/Components/NewWeeklyReport.tsx | 36 +++++++++++---------- frontend/src/Pages/YourProjectsPage.tsx | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index 86322e7..ab9084d 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -1,17 +1,17 @@ import { useState } from "react"; -import { NewWeeklyReport } from "../Types/goTypes"; +import type { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; export default function NewWeeklyReport(): JSX.Element { - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); + const [week, setWeek] = useState(); + const [developmentTime, setDevelopmentTime] = useState(); + const [meetingTime, setMeetingTime] = useState(); + const [adminTime, setAdminTime] = useState(); + const [ownWorkTime, setOwnWorkTime] = useState(); + const [studyTime, setStudyTime] = useState(); + const [testingTime, setTestingTime] = useState(); const { projectName } = useParams(); const token = localStorage.getItem("accessToken") ?? ""; @@ -19,13 +19,13 @@ export default function NewWeeklyReport(): JSX.Element { const handleNewWeeklyReport = async (): Promise => { const newWeeklyReport: NewWeeklyReport = { projectName: projectName ?? "", - week, - developmentTime, - meetingTime, - adminTime, - ownWorkTime, - studyTime, - testingTime, + week: week ?? 0, + developmentTime: developmentTime ?? 0, + meetingTime: meetingTime ?? 0, + adminTime: adminTime ?? 0, + ownWorkTime: ownWorkTime ?? 0, + studyTime: studyTime ?? 0, + testingTime: testingTime ?? 0, }; await api.submitWeeklyReport(newWeeklyReport, token); @@ -45,7 +45,7 @@ export default function NewWeeklyReport(): JSX.Element { } e.preventDefault(); void handleNewWeeklyReport(); - navigate(-1); + navigate("/project"); }} >
@@ -58,7 +58,9 @@ export default function NewWeeklyReport(): JSX.Element { setWeek(weekNumber); }} onKeyDown={(event) => { - event.preventDefault(); + const keyValue = event.key; + if (!/\d/.test(keyValue) && keyValue !== "Backspace") + event.preventDefault(); }} onPaste={(event) => { event.preventDefault(); diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index b8034f3..a3cd47a 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -22,7 +22,7 @@ function UserProjectPage(): JSX.Element { }; // Call getProjects when the component mounts useEffect(() => { - getProjects(); + void getProjects(); }, []); const handleProjectClick = (projectName: string): void => { From 4dbbee324922e86a9a96d6ac05df7d3969bf3525 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 19 Mar 2024 02:14:09 +0100 Subject: [PATCH 20/32] Checking errors from transactions in go --- backend/internal/database/db.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index ad408a7..bc6e1e8 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -204,15 +204,22 @@ func (d *Db) AddProject(name string, description string, username string) error tx := d.MustBegin() _, err := tx.Exec(projectInsert, name, description, username) if err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return err + } return err } _, err = tx.Exec(changeUserRole, "project_manager", username, name) if err != nil { - tx.Rollback() + if err := tx.Rollback(); err != nil { + return err + } return err } - tx.Commit() + if err := tx.Commit(); err != nil { + return err + } + return err } From 08532444f089c17f077d4513a2dfd8d04195a5fc Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 09:14:30 +0100 Subject: [PATCH 21/32] Cleanup YourProjectsPage --- frontend/src/Pages/YourProjectsPage.tsx | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/frontend/src/Pages/YourProjectsPage.tsx b/frontend/src/Pages/YourProjectsPage.tsx index 0f2428e..b5644c3 100644 --- a/frontend/src/Pages/YourProjectsPage.tsx +++ b/frontend/src/Pages/YourProjectsPage.tsx @@ -1,13 +1,11 @@ -import { useState, createContext } from "react"; +import { useState, useEffect } from "react"; import { Project } from "../Types/goTypes"; import { Link } from "react-router-dom"; import BasicWindow from "../Components/BasicWindow"; - -export const ProjectNameContext = createContext(""); +import { api } from "../API/API"; function UserProjectPage(): JSX.Element { const [projects, setProjects] = useState([]); - const [selectedProject, setSelectedProject] = useState(""); const getProjects = async (): Promise => { const token = localStorage.getItem("accessToken") ?? ""; @@ -24,29 +22,19 @@ function UserProjectPage(): JSX.Element { void getProjects(); }, []); - const handleProjectClick = (projectName: string): void => { - setSelectedProject(projectName); - }; - const content = ( - + <>

Your Projects

{projects.map((project, index) => ( - { - handleProjectClick(project.name); - }} - key={index} - > +

{project.name}

))}
-
+ ); const buttons = <>; From d775a6e381f7d2ccac0c1b035ae1b540d7238ee3 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 09:21:33 +0100 Subject: [PATCH 22/32] Update navigation in NewWeeklyReport component --- frontend/src/Components/NewWeeklyReport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index ab9084d..b8a6f7e 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -45,7 +45,7 @@ export default function NewWeeklyReport(): JSX.Element { } e.preventDefault(); void handleNewWeeklyReport(); - navigate("/project"); + navigate(-1); }} >
From 53f468d7c89b791428da33aff193d707ca4823a3 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 09:23:09 +0100 Subject: [PATCH 23/32] Update navigation in EditWeeklyReport component --- frontend/src/Components/EditWeeklyReport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx index b0e8771..3da3b86 100644 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -82,7 +82,7 @@ export default function GetWeeklyReport(): JSX.Element { } e.preventDefault(); void handleNewWeeklyReport(); - navigate("/project"); + navigate(-1); }} >
From 4f53f9de94e5b83361c3b0cc8dd6778f0fc08eb0 Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 09:33:20 +0100 Subject: [PATCH 24/32] Fix initial value of week state in NewWeeklyReport component for error message --- frontend/src/Components/NewWeeklyReport.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx index b8a6f7e..e53823d 100644 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ b/frontend/src/Components/NewWeeklyReport.tsx @@ -5,7 +5,7 @@ import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; export default function NewWeeklyReport(): JSX.Element { - const [week, setWeek] = useState(); + const [week, setWeek] = useState(0); const [developmentTime, setDevelopmentTime] = useState(); const [meetingTime, setMeetingTime] = useState(); const [adminTime, setAdminTime] = useState(); @@ -19,7 +19,7 @@ export default function NewWeeklyReport(): JSX.Element { const handleNewWeeklyReport = async (): Promise => { const newWeeklyReport: NewWeeklyReport = { projectName: projectName ?? "", - week: week ?? 0, + week: week, developmentTime: developmentTime ?? 0, meetingTime: meetingTime ?? 0, adminTime: adminTime ?? 0, From cda2b72d08031330b3d7f5676815df381f627a35 Mon Sep 17 00:00:00 2001 From: Mattias Date: Tue, 19 Mar 2024 09:34:37 +0100 Subject: [PATCH 25/32] projectName is now fetched from params --- frontend/src/Components/EditWeeklyReport.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx index 3da3b86..0bfc608 100644 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ b/frontend/src/Components/EditWeeklyReport.tsx @@ -1,11 +1,10 @@ import { useState, useEffect } from "react"; import { NewWeeklyReport } from "../Types/goTypes"; import { api } from "../API/API"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import Button from "./Button"; export default function GetWeeklyReport(): JSX.Element { - const [projectName, setProjectName] = useState(""); const [week, setWeek] = useState(0); const [developmentTime, setDevelopmentTime] = useState(0); const [meetingTime, setMeetingTime] = useState(0); @@ -16,12 +15,13 @@ export default function GetWeeklyReport(): JSX.Element { const token = localStorage.getItem("accessToken") ?? ""; const username = localStorage.getItem("username") ?? ""; + const { projectName } = useParams(); useEffect(() => { const fetchWeeklyReport = async (): Promise => { const response = await api.getWeeklyReport( username, - projectName, + projectName ?? "", week.toString(), token, ); @@ -37,7 +37,7 @@ export default function GetWeeklyReport(): JSX.Element { studyTime: 0, testingTime: 0, }; - setProjectName(report.projectName); + setWeek(report.week); setDevelopmentTime(report.developmentTime); setMeetingTime(report.meetingTime); @@ -55,7 +55,7 @@ export default function GetWeeklyReport(): JSX.Element { const handleNewWeeklyReport = async (): Promise => { const newWeeklyReport: NewWeeklyReport = { - projectName, + projectName: projectName ?? "", week, developmentTime, meetingTime, From a30a6a498899f04ddceacd9b635211ef0062603c Mon Sep 17 00:00:00 2001 From: Davenludd Date: Tue, 19 Mar 2024 09:37:13 +0100 Subject: [PATCH 26/32] Fix button UserNewTimeReportPage --- .../src/Pages/UserPages/UserNewTimeReportPage.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx index 2cdeb15..cd69b3b 100644 --- a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx @@ -1,7 +1,6 @@ +import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; import NewWeeklyReport from "../../Components/NewWeeklyReport"; -import { Link } from "react-router-dom"; function UserNewTimeReportPage(): JSX.Element { const content = ( @@ -13,15 +12,7 @@ function UserNewTimeReportPage(): JSX.Element { const buttons = ( <> - -