unsignReport handler + API function
This commit is contained in:
parent
f57c445ead
commit
a5e3d4259d
7 changed files with 264 additions and 0 deletions
|
@ -45,6 +45,7 @@ type Database interface {
|
||||||
UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime 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
|
RemoveProject(projectname string) error
|
||||||
GetUserName(id int) (string, error)
|
GetUserName(id int) (string, error)
|
||||||
|
UnsignWeeklyReport(reportId int, projectManagerId int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// This struct is a wrapper type that holds the database connection
|
// This struct is a wrapper type that holds the database connection
|
||||||
|
@ -372,6 +373,36 @@ func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Db) UnsignWeeklyReport(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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 unsign the report")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the signed_by field of the specified report
|
||||||
|
_, err = d.Exec("UPDATE weekly_reports SET signed_by = NULL WHERE report_id = ?;", projectManagerId, reportId)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Db) GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) {
|
func (d *Db) GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) {
|
||||||
// Define the SQL query to fetch unsigned reports for a given user
|
// Define the SQL query to fetch unsigned reports for a given user
|
||||||
query := `
|
query := `
|
||||||
|
|
|
@ -583,6 +583,94 @@ func TestSignWeeklyReport(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnsignWeeklyReport(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsign the report
|
||||||
|
err = db.UnsignWeeklyReport(report.ReportId, projectManagerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("UnsignWeeklyReport failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the report again to check if it's unsigned
|
||||||
|
unsignedReport, err := db.GetWeeklyReport("testuser", "testproject", 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("GetWeeklyReport failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the report is unsigned
|
||||||
|
if unsignedReport.SignedBy != nil {
|
||||||
|
t.Error("Expected SignedBy to be nil, got", unsignedReport.SignedBy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestSignWeeklyReportByAnotherProjectManager tests the scenario where a project manager attempts to sign a weekly report for a user who is not assigned to their project
|
// TestSignWeeklyReportByAnotherProjectManager tests the scenario where a project manager attempts to sign a weekly report for a user who is not assigned to their project
|
||||||
func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) {
|
func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) {
|
||||||
db, err := setupState()
|
db, err := setupState()
|
||||||
|
|
41
backend/internal/handlers/reports/UnsignReport.go
Normal file
41
backend/internal/handlers/reports/UnsignReport.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package reports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
db "ttime/internal/database"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/log"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UnsignReport(c *fiber.Ctx) error {
|
||||||
|
// Extract the necessary parameters from the token
|
||||||
|
user := c.Locals("user").(*jwt.Token)
|
||||||
|
claims := user.Claims.(jwt.MapClaims)
|
||||||
|
projectManagerUsername := claims["name"].(string)
|
||||||
|
|
||||||
|
// Extract report ID from the path
|
||||||
|
reportId, err := strconv.Atoi(c.Params("reportId"))
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Invalid report ID")
|
||||||
|
return c.Status(400).SendString("Invalid report ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the project manager's ID
|
||||||
|
projectManagerID, err := db.GetDb(c).GetUserId(projectManagerUsername)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to get project manager ID for user: ", projectManagerUsername)
|
||||||
|
return c.Status(500).SendString("Failed to get project manager ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the database function to sign the weekly report
|
||||||
|
err = db.GetDb(c).UnsignWeeklyReport(reportId, projectManagerID)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Error Unsigning weekly report:", err)
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Project manager ID: ", projectManagerID, " signed report ID: ", reportId)
|
||||||
|
return c.Status(200).SendString("Weekly report unsigned successfully")
|
||||||
|
}
|
|
@ -126,6 +126,7 @@ func main() {
|
||||||
api.Delete("/removeProject/:projectName", projects.RemoveProject)
|
api.Delete("/removeProject/:projectName", projects.RemoveProject)
|
||||||
api.Delete("/project/:projectID", projects.DeleteProject)
|
api.Delete("/project/:projectID", projects.DeleteProject)
|
||||||
|
|
||||||
|
|
||||||
// All report related routes
|
// All report related routes
|
||||||
// reportGroup := api.Group("/report") // Not currently in use
|
// reportGroup := api.Group("/report") // Not currently in use
|
||||||
api.Get("/getWeeklyReport", reports.GetWeeklyReport)
|
api.Get("/getWeeklyReport", reports.GetWeeklyReport)
|
||||||
|
@ -134,6 +135,7 @@ func main() {
|
||||||
api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport)
|
api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport)
|
||||||
api.Put("/signReport/:reportId", reports.SignReport)
|
api.Put("/signReport/:reportId", reports.SignReport)
|
||||||
api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport)
|
api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport)
|
||||||
|
api.Put("/unsignReport/:reportId", reports.UnsignReport)
|
||||||
|
|
||||||
// Announce the port we are listening on and start the server
|
// Announce the port we are listening on and start the server
|
||||||
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
||||||
|
|
|
@ -221,6 +221,15 @@ interface API {
|
||||||
*/
|
*/
|
||||||
signReport(reportId: number, token: string): Promise<APIResponse<string>>;
|
signReport(reportId: number, token: string): Promise<APIResponse<string>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsigns a report. Keep in mind that the user which the token belongs to must be
|
||||||
|
* the project manager of the project the report belongs to.
|
||||||
|
*
|
||||||
|
* @param {number} reportId The id of the report to sign
|
||||||
|
* @param {string} token The authentication token
|
||||||
|
*/
|
||||||
|
unsignReport(reportId: number, token: string): Promise<APIResponse<string>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promotes a user to project manager within a project.
|
* Promotes a user to project manager within a project.
|
||||||
*
|
*
|
||||||
|
@ -846,6 +855,29 @@ export const api: API = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async unsignReport(
|
||||||
|
reportId: number,
|
||||||
|
token: string,
|
||||||
|
): Promise<APIResponse<string>> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/unsignReport/${reportId}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: "Bearer " + token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return { success: false, message: "Failed to unsign report" };
|
||||||
|
} else {
|
||||||
|
return { success: true, message: "Report unsigned" };
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return { success: false, message: "Failed to unsign report" };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async promoteToPm(
|
async promoteToPm(
|
||||||
userName: string,
|
userName: string,
|
||||||
projectName: string,
|
projectName: string,
|
||||||
|
|
|
@ -34,6 +34,7 @@ getChangeUserNamePath = base_url + "/api/changeUserName"
|
||||||
getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport"
|
getUpdateWeeklyReportPath = base_url + "/api/updateWeeklyReport"
|
||||||
removeProjectPath = base_url + "/api/removeProject"
|
removeProjectPath = base_url + "/api/removeProject"
|
||||||
promoteToPmPath = base_url + "/api/promoteToPm"
|
promoteToPmPath = base_url + "/api/promoteToPm"
|
||||||
|
unsignReportPath = base_url + "/api/unsignReport"
|
||||||
|
|
||||||
debug_output = False
|
debug_output = False
|
||||||
|
|
||||||
|
@ -149,3 +150,9 @@ def signReport(project_manager_token: string, report_id: int):
|
||||||
signReportPath + "/" + str(report_id),
|
signReportPath + "/" + str(report_id),
|
||||||
headers={"Authorization": "Bearer " + project_manager_token},
|
headers={"Authorization": "Bearer " + project_manager_token},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def unsignReport(project_manager_token: string, report_id: int):
|
||||||
|
return requests.put(
|
||||||
|
unsignReportPath + "/" + str(report_id),
|
||||||
|
headers={"Authorization": "Bearer " + project_manager_token},
|
||||||
|
)
|
||||||
|
|
|
@ -215,6 +215,68 @@ def test_sign_report():
|
||||||
assert report_id != None, "Get report failed"
|
assert report_id != None, "Get report failed"
|
||||||
gprint("test_sign_report successful")
|
gprint("test_sign_report successful")
|
||||||
|
|
||||||
|
# Test function to unsign a report
|
||||||
|
def test_unsign_report():
|
||||||
|
# Pm user
|
||||||
|
pm_username = "pm" + randomString()
|
||||||
|
pm_password = "admin_password2"
|
||||||
|
|
||||||
|
# User to add
|
||||||
|
member_user = "member" + randomString()
|
||||||
|
member_passwd = "password"
|
||||||
|
|
||||||
|
# Name of the project to be created
|
||||||
|
project_name = "project" + randomString()
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Submit a report for the project
|
||||||
|
response = submitReport(
|
||||||
|
member_token,
|
||||||
|
{
|
||||||
|
"projectName": project_name,
|
||||||
|
"week": 1,
|
||||||
|
"developmentTime": 10,
|
||||||
|
"meetingTime": 5,
|
||||||
|
"adminTime": 5,
|
||||||
|
"ownWorkTime": 10,
|
||||||
|
"studyTime": 10,
|
||||||
|
"testingTime": 10,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, "Submit report failed"
|
||||||
|
|
||||||
|
# Retrieve the report ID
|
||||||
|
report_id = getReport(member_token, member_user, project_name)["reportId"]
|
||||||
|
|
||||||
|
# Sign the report as the project manager
|
||||||
|
response = signReport(pm_token, report_id)
|
||||||
|
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"
|
||||||
|
|
||||||
|
# Unsign the report as the project manager
|
||||||
|
response = unsignReport(pm_token, report_id)
|
||||||
|
assert response.status_code == 200, "Unsign report failed"
|
||||||
|
dprint("Unsign 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"
|
||||||
|
gprint("test_unsign_report successful")
|
||||||
|
|
||||||
|
|
||||||
# Test function to get weekly reports for a user in a project
|
# Test function to get weekly reports for a user in a project
|
||||||
def test_get_all_weekly_reports():
|
def test_get_all_weekly_reports():
|
||||||
|
@ -497,6 +559,7 @@ def test_promote_to_manager():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
test_unsign_report()
|
||||||
test_promote_to_manager()
|
test_promote_to_manager()
|
||||||
test_remove_project()
|
test_remove_project()
|
||||||
test_get_user_projects()
|
test_get_user_projects()
|
||||||
|
|
Loading…
Reference in a new issue