Compare commits

..

No commits in common. "5696310a687e7d59a5469fe870074034164b42fe" and "1a412dc8a06b335c1d7d50b5311d465f0bffa3be" have entirely different histories.

10 changed files with 40 additions and 229 deletions

View file

@ -2,7 +2,6 @@ package database
import (
"embed"
"errors"
"path/filepath"
"ttime/internal/types"
@ -31,7 +30,6 @@ 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)
SignWeeklyReport(reportId int, projectManagerId int) error
}
// This struct is a wrapper type that holds the database connection
@ -272,8 +270,7 @@ func (d *Db) GetWeeklyReport(username string, projectName string, week int) (typ
admin_time,
own_work_time,
study_time,
testing_time,
signed_by
testing_time
FROM
weekly_reports
WHERE
@ -285,34 +282,6 @@ func (d *Db) GetWeeklyReport(username string, projectName string, week int) (typ
return report, err
}
// SignWeeklyReport signs a weekly report by updating the signed_by field
// with the provided project manager's ID, but only if the project manager
// is in the same project as the report
func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error {
// Retrieve the project ID associated with the report
var reportProjectID int
err := d.Get(&reportProjectID, "SELECT project_id FROM weekly_reports WHERE report_id = ?", reportId)
if err != nil {
return err
}
// Retrieve the project ID associated with the project manager
var managerProjectID int
err = d.Get(&managerProjectID, "SELECT project_id FROM user_roles WHERE user_id = ? AND p_role = 'project_manager'", projectManagerId)
if err != nil {
return err
}
// Check if the project manager is in the same project as the report
if reportProjectID != managerProjectID {
return errors.New("project manager doesn't have permission to sign the report")
}
// Update the signed_by field of the specified report
_, err = d.Exec("UPDATE weekly_reports SET signed_by = ? WHERE report_id = ?", projectManagerId, reportId)
return err
}
// Reads a directory of migration files and applies them to the database.
// This will eventually be used on an embedded directory
func (d *Db) Migrate() error {

View file

@ -1,7 +1,6 @@
package database
import (
"fmt"
"testing"
)
@ -411,128 +410,3 @@ func TestGetWeeklyReport(t *testing.T) {
}
// Check other fields similarly
}
func TestSignWeeklyReport(t *testing.T) {
db, err := setupState()
if err != nil {
t.Error("setupState failed:", err)
}
// Add project manager
err = db.AddUser("projectManager", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
// Add a regular user
err = db.AddUser("testuser", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
// Add project
err = db.AddProject("testproject", "description", "projectManager")
if err != nil {
t.Error("AddProject failed:", err)
}
// Add both regular users as members to the project
err = db.AddUserToProject("testuser", "testproject", "member")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
err = db.AddUserToProject("projectManager", "testproject", "project_manager")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
// Add a weekly report for one of the regular users
err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1)
if err != nil {
t.Error("AddWeeklyReport failed:", err)
}
// Retrieve the added report
report, err := db.GetWeeklyReport("testuser", "testproject", 1)
if err != nil {
t.Error("GetWeeklyReport failed:", err)
}
// Print project manager's ID
projectManagerID, err := db.GetUserId("projectManager")
if err != nil {
t.Error("GetUserId failed:", err)
}
fmt.Println("Project Manager's ID:", projectManagerID)
// Sign the report with the project manager
err = db.SignWeeklyReport(report.ReportId, projectManagerID)
if err != nil {
t.Error("SignWeeklyReport failed:", err)
}
// Retrieve the report again to check if it's signed
signedReport, err := db.GetWeeklyReport("testuser", "testproject", 1)
if err != nil {
t.Error("GetWeeklyReport failed:", err)
}
// Ensure the report is signed by the project manager
if *signedReport.SignedBy != projectManagerID {
t.Errorf("Expected SignedBy to be %d, got %d", projectManagerID, *signedReport.SignedBy)
}
}
func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) {
db, err := setupState()
if err != nil {
t.Error("setupState failed:", err)
}
// Add project manager
err = db.AddUser("projectManager", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
// Add a regular user
err = db.AddUser("testuser", "password")
if err != nil {
t.Error("AddUser failed:", err)
}
// Add project
err = db.AddProject("testproject", "description", "projectManager")
if err != nil {
t.Error("AddProject failed:", err)
}
// Add the regular user as a member to the project
err = db.AddUserToProject("testuser", "testproject", "member")
if err != nil {
t.Error("AddUserToProject failed:", err)
}
// Add a weekly report for the regular user
err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1)
if err != nil {
t.Error("AddWeeklyReport failed:", err)
}
// Retrieve the added report
report, err := db.GetWeeklyReport("testuser", "testproject", 1)
if err != nil {
t.Error("GetWeeklyReport failed:", err)
}
anotherManagerID, err := db.GetUserId("projectManager")
if err != nil {
t.Error("GetUserId failed:", err)
}
err = db.SignWeeklyReport(report.ReportId, anotherManagerID)
if err == nil {
t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't")
}
}

View file

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS weekly_reports (
CREATE TABLE weekly_reports (
report_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
project_id INTEGER NOT NULL,

View file

@ -22,16 +22,13 @@ import (
func (gs *GState) Register(c *fiber.Ctx) error {
u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
println("Error parsing body")
return c.Status(400).SendString(err.Error())
}
println("Adding user:", u.Username)
if err := gs.Db.AddUser(u.Username, u.Password); err != nil {
return c.Status(500).SendString(err.Error())
}
println("User added:", u.Username)
return c.Status(200).SendString("User added")
}
@ -60,11 +57,9 @@ 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")
return c.Status(400).SendString(err.Error())
}
println("Username:", u.Username)
if !gs.Db.CheckUser(u.Username, u.Password) {
println("User not found")
return c.SendStatus(fiber.StatusUnauthorized)
@ -79,16 +74,13 @@ func (gs *GState) Login(c *fiber.Ctx) error {
// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
println("Token created for user:", u.Username)
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("secret"))
if err != nil {
println("Error signing token")
return c.SendStatus(fiber.StatusInternalServerError)
}
println("Successfully signed token for user:", u.Username)
return c.JSON(fiber.Map{"token": t})
}

View file

@ -41,6 +41,4 @@ type WeeklyReport struct {
StudyTime int `json:"studyTime" db:"study_time"`
// Total time spent on testing
TestingTime int `json:"testingTime" db:"testing_time"`
// The project manager who signed it
SignedBy *int `json:"signedBy" db:"signed_by"`
}

View file

@ -8,8 +8,9 @@ describe("API", () => {
username: "lol", // Add the username property
password: "lol",
};
const response = await api.registerUser(user);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("userId");
});
@ -23,7 +24,7 @@ describe("API", () => {
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const response = await api.createProject(project, token);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("projectId");
});
@ -33,7 +34,7 @@ describe("API", () => {
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const response = await api.renewToken(refreshToken);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("accessToken");
expect(response.data).toHaveProperty("refreshToken");
@ -44,7 +45,7 @@ describe("API", () => {
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const username = "rrgumdzpmc";
const response = await api.getUserProjects(username, token);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("projects");
});
@ -64,7 +65,7 @@ describe("API", () => {
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t";
const response = await api.submitWeeklyReport(report, token);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty(
"message",
@ -79,7 +80,7 @@ describe("API", () => {
};
const response = await api.login(user);
console.log(response.message);
expect(response.success).toBe(true);
expect(response.data).toHaveProperty("accessToken");
expect(response.data).toHaveProperty("refreshToken");

View file

@ -7,7 +7,7 @@ import {
} from "../Types/goTypes";
// This type of pattern should be hard to misuse
export interface APIResponse<T> {
interface APIResponse<T> {
success: boolean;
message?: string;
data?: T;

View file

@ -1,54 +1,34 @@
import { NewUser } from "../Types/goTypes";
import { api, APIResponse } from "../API/API";
import { Dispatch, SetStateAction } from "react";
/*
* Checks if user is in database with api.login and then sets proper authority level
* TODO: change so that it checks for user type (admin, user, pm) somehow instead
**/
function LoginCheck(props: {
username: string;
password: string;
setAuthority: Dispatch<SetStateAction<number>>;
}): number {
const user: NewUser = {
username: props.username,
password: props.password,
function LoginCheck(props: { username: string; password: string }): number {
//Example users for testing without backend, remove when using backend
const admin: NewUser = {
username: "admin",
password: "123",
};
const pmanager: NewUser = {
username: "pmanager",
password: "123",
};
const user: NewUser = {
username: "user",
password: "123",
};
api
.login(user)
.then((response: APIResponse<string>) => {
if (response.success) {
if (response.data !== undefined) {
const token = response.data;
//TODO: change so that it checks for user type (admin, user, pm) instead
if (token !== "" && props.username === "admin") {
props.setAuthority((prevAuth) => {
prevAuth = 1;
return prevAuth;
});
} else if (token !== "" && props.username === "pm") {
props.setAuthority((prevAuth) => {
prevAuth = 2;
return prevAuth;
});
} else if (token !== "" && props.username === "user") {
props.setAuthority((prevAuth) => {
prevAuth = 3;
return prevAuth;
});
}
} else {
console.error("Token was undefined");
}
} else {
console.error("Token could not be fetched");
}
})
.catch((error) => {
console.error("An error occurred during login:", error);
});
//TODO: Compare with db instead when finished
if (props.username === admin.username && props.password === admin.password) {
return 1;
} else if (
props.username === pmanager.username &&
props.password === pmanager.password
) {
return 2;
} else if (
props.username === user.username &&
props.password === user.password
) {
return 3;
}
return 0;
}

View file

@ -15,10 +15,9 @@ function LoginPage(props: {
and if so, redirect to correct page */
function handleSubmit(event: FormEvent<HTMLFormElement>): void {
event.preventDefault();
LoginCheck({
username: username,
password: password,
setAuthority: props.setAuthority,
props.setAuthority((prevAuth) => {
prevAuth = LoginCheck({ username: username, password: password });
return prevAuth;
});
}

View file

@ -14,7 +14,6 @@ function PMProjectMembers(): JSX.Element {
onClick={(): void => {
return;
}}
type={"button"}
/>
</Link>
<Link to="/PM-time-role">
@ -23,7 +22,6 @@ function PMProjectMembers(): JSX.Element {
onClick={(): void => {
return;
}}
type={"button"}
/>
</Link>
<BackButton />