Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

Commit

Permalink
Merge pull request #704 from SELab-2/development
Browse files Browse the repository at this point in the history
FINAL merge 🀞
  • Loading branch information
BramDevlaminck authored May 22, 2022
2 parents 12c0190 + 7422ec3 commit bc22a67
Show file tree
Hide file tree
Showing 37 changed files with 642 additions and 265 deletions.
4 changes: 2 additions & 2 deletions backend/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"apiErrors": {
"invalidID": {
"http": 204,
"http": 423,
"reason": "This endpoint requires an ID. The ID you provided was invalid."
},
"argumentError": {
Expand Down Expand Up @@ -95,7 +95,7 @@
],
"preferred": "/api-osoc",
"authScheme": "auth/osoc2",
"defaultUserId": 3,
"defaultUserId": 1,
"port": 4096,
"pageSize": 10
},
Expand Down
2 changes: 1 addition & 1 deletion backend/orm_functions/project_role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export async function updateProjectRole(projectRole: UpdateProjectRole) {
*
* @param projectRoleId the projectRole we are deleting from the project
* role-table
* @returns TODO: what does this return?
* @returns a promise with the deleted record
*/
export async function deleteProjectRole(projectRoleId: number) {
const result = await prisma.project_role.delete({
Expand Down
9 changes: 8 additions & 1 deletion backend/orm_functions/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "./orm_types";
import { getOsocYearsForLoginUser } from "./login_user";
import { deletePersonFromDB } from "./person";
import { Decision } from "../types";

/**
*
Expand Down Expand Up @@ -179,7 +180,13 @@ export async function filterStudents(
// manually create filter object for evaluation because evaluation doesn't need to exist
// and then the whole object needs to be undefined
let evaluationFilter;
if (statusFilter) {
if ((statusFilter as Decision) === Decision.NONE) {
evaluationFilter = {
none: {
is_final: true,
},
};
} else if (statusFilter) {
evaluationFilter = {
some: {
decision: statusFilter,
Expand Down
5 changes: 3 additions & 2 deletions backend/routes/followup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as ormJA from "../orm_functions/job_application";
import * as rq from "../request";
import { Responses } from "../types";
import * as util from "../utility";
import { checkYearPermissionStudent, errors } from "../utility";
import { checkYearPermissionsFollowup, errors } from "../utility";
import { getOsocYearsForLoginUser } from "../orm_functions/login_user";
import { getLatestOsoc, getOsocById } from "../orm_functions/osoc";
import { getJobApplication } from "../orm_functions/job_application";
Expand All @@ -21,6 +21,7 @@ export async function getFollowup(
return rq
.parseGetFollowupStudentRequest(req)
.then((parsed) => util.checkSessionKey(parsed))
.then(checkYearPermissionsFollowup)
.then((checked) =>
ormJA
.getJobApplication(checked.data.id)
Expand Down Expand Up @@ -59,7 +60,7 @@ export async function updateFollowup(
return rq
.parseSetFollowupStudentRequest(req)
.then((parsed) => util.checkSessionKey(parsed))
.then(checkYearPermissionStudent)
.then(checkYearPermissionsFollowup)
.then(async (checked) => {
// modifications to a job application is only allowed if the job application is of the most recent osoc year
const [jobApplication, latestOsoc] = await Promise.all([
Expand Down
9 changes: 4 additions & 5 deletions backend/routes/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export async function getProject(
): Promise<Responses.ProjectAndContracts> {
const parsedRequest = await rq.parseSingleProjectRequest(req);
const checkedId = await util
.isAdmin(parsedRequest)
.checkSessionKey(parsedRequest)
.then(checkYearPermissionProject)
.then((v) => util.isValidID(v.data, "project"));

Expand Down Expand Up @@ -506,7 +506,7 @@ export async function unAssignCoach(
): Promise<Responses.Empty> {
return rq
.parseRemoveCoachRequest(req)
.then((parsed) => util.checkSessionKey(parsed))
.then((parsed) => util.isAdmin(parsed))
.then(async (checked) => {
const project = await ormPr.getProjectById(checked.data.id);

Expand Down Expand Up @@ -555,7 +555,7 @@ export async function assignCoach(
): Promise<Responses.ProjectUser> {
return rq
.parseAssignCoachRequest(req)
.then((parsed) => util.checkSessionKey(parsed))
.then((parsed) => util.isAdmin(parsed))
.then(async (checked) => {
const project = await ormPr.getProjectById(checked.data.id);

Expand Down Expand Up @@ -701,7 +701,7 @@ export async function assignStudent(
// authenticate, parse, ...
const checked = await rq
.parseDraftStudentRequest(req)
.then((parsed) => util.isAdmin(parsed));
.then((parsed) => util.checkSessionKey(parsed));

// check if edition is ready
const latestOsoc = await ormOsoc
Expand Down Expand Up @@ -900,7 +900,6 @@ export function getRouter(): express.Router {
util.route(router, "delete", "/:id/coach", unAssignCoach);
util.route(router, "post", "/:id/coach", assignCoach);

// TODO add project conflicts
util.addAllInvalidVerbs(router, [
"/",
"/all",
Expand Down
1 change: 0 additions & 1 deletion backend/tests/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ test("Can parse login request", () => {
unvalid.body.pass = "Pass #2";
unvalid.body.name = "Name.email.be";

// TODO
return Promise.all([
expect(Rq.parseLoginRequest(valid)).resolves.toStrictEqual({
name: "[email protected]",
Expand Down
7 changes: 6 additions & 1 deletion backend/tests/routes_unit/followup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ jest.mock("../../utility", () => {
checkSessionKey: jest.fn(),
isAdmin: jest.fn(),
checkYearPermissionStudent: jest.fn(),
}; // we want to only mock checkSessionKey, isAdmin and checkYearPermissionStudent
checkYearPermissionsFollowup: jest.fn(),
}; // we want to only mock checkSessionKey, isAdmin, checkYearPermissionStudent and checkYearPermissionsFollowup
});
export const utilMock = util as jest.Mocked<typeof util>;

Expand Down Expand Up @@ -179,6 +180,9 @@ beforeEach(() => {
utilMock.checkYearPermissionStudent.mockImplementation((v) =>
Promise.resolve(v)
);
utilMock.checkYearPermissionsFollowup.mockImplementation((v) =>
Promise.resolve(v)
);

osocMock.getLatestOsoc.mockResolvedValue(osocdat);
osocMock.getOsocById.mockResolvedValue(osocdat);
Expand Down Expand Up @@ -210,6 +214,7 @@ afterEach(() => {
utilMock.checkSessionKey.mockReset();
utilMock.isAdmin.mockReset();
utilMock.checkYearPermissionStudent.mockReset();
utilMock.checkYearPermissionsFollowup.mockReset();

osocMock.getLatestOsoc.mockReset();
osocMock.getOsocById.mockReset();
Expand Down
22 changes: 11 additions & 11 deletions backend/tests/routes_unit/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ test("Can get single project", async () => {

await expect(project.getProject(req)).resolves.toStrictEqual(res);
expectCall(reqMock.parseSingleProjectRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(utilMock.isValidID).toHaveBeenCalledTimes(1);
expectCall(ormPrMock.getProjectById, 0);
expectCall(ormCMock.contractsByProject, 0);
Expand All @@ -956,7 +956,7 @@ test("Can't get single project (role error)", async () => {

await expect(project.getProject(req)).rejects.toStrictEqual(undefined);
expectCall(reqMock.parseSingleProjectRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(utilMock.isValidID).toHaveBeenCalledTimes(1);
expectCall(ormPrMock.getProjectById, 0);
expectCall(ormCMock.contractsByProject, 0);
Expand All @@ -978,7 +978,7 @@ test("Can't get single project (ID error)", async () => {

await expect(project.getProject(req)).rejects.toStrictEqual(undefined);
expectCall(reqMock.parseSingleProjectRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(utilMock.isValidID).toHaveBeenCalledTimes(1);
expectCall(ormPrMock.getProjectById, 0);
expect(ormCMock.contractsByProject).not.toHaveBeenCalled();
Expand Down Expand Up @@ -1242,7 +1242,7 @@ test("Can un-assign coaches", async () => {
// await project.unAssignCoach(req);
await expect(project.unAssignCoach(req)).resolves.toStrictEqual({});
expectCall(reqMock.parseRemoveCoachRequest, req);
expectCall(utilMock.checkSessionKey, req.body);
expectCall(utilMock.isAdmin, req.body);
expectCall(ormPUMock.getUsersFor, 0);
expectCall(ormPUMock.deleteProjectUser, {
loginUserId: req.body.loginUserId,
Expand All @@ -1263,7 +1263,7 @@ test("Can't un-assign coaches (invalid id)", async () => {
reason: "The coach with ID 7 is not assigned to project 0",
});
expectCall(reqMock.parseRemoveCoachRequest, req);
expectCall(utilMock.checkSessionKey, req.body);
expectCall(utilMock.isAdmin, req.body);
expectCall(ormPUMock.getUsersFor, 0);
expect(ormPUMock.deleteProjectUser).not.toHaveBeenCalled();
});
Expand All @@ -1282,7 +1282,7 @@ test("Can assign coaches", async () => {
project_id: 0,
});
expectCall(reqMock.parseAssignCoachRequest, req);
expectCall(utilMock.checkSessionKey, req.body);
expectCall(utilMock.isAdmin, req.body);
expectCall(ormPUMock.getUsersFor, 0);
expectCall(ormPUMock.createProjectUser, { projectId: 0, loginUserId: 7 });
});
Expand All @@ -1300,7 +1300,7 @@ test("Can't assign coaches (already assigned)", async () => {
reason: "The coach with ID 0 is already assigned to project 0",
});
expectCall(reqMock.parseAssignCoachRequest, req);
expectCall(utilMock.checkSessionKey, req.body);
expectCall(utilMock.isAdmin, req.body);
expectCall(ormPUMock.getUsersFor, 0);
expect(ormPUMock.createProjectUser).not.toHaveBeenCalled();
});
Expand Down Expand Up @@ -1401,7 +1401,7 @@ test("Can assign students", async () => {
role: "dev",
});
expectCall(reqMock.parseDraftStudentRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(ormOMock.getLatestOsoc).toHaveBeenCalledTimes(1);
expectCall(ormCMock.contractsForStudent, req.body.studentId);
expectCall(ormCMock.createContract, {
Expand Down Expand Up @@ -1460,7 +1460,7 @@ test("Can't assign students (no places)", async () => {
reason: "There are no more free spaces for that role",
});
expectCall(reqMock.parseDraftStudentRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(ormOMock.getLatestOsoc).toHaveBeenCalledTimes(1);
expectCall(ormCMock.contractsForStudent, req.body.studentId);
expect(ormCMock.createContract).not.toHaveBeenCalled();
Expand Down Expand Up @@ -1514,7 +1514,7 @@ test("Can't assign students (no such role)", async () => {
reason: "That role doesn't exist",
});
expectCall(reqMock.parseDraftStudentRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(ormOMock.getLatestOsoc).toHaveBeenCalledTimes(1);
expectCall(ormCMock.contractsForStudent, req.body.studentId);
expect(ormCMock.createContract).not.toHaveBeenCalled();
Expand Down Expand Up @@ -1571,7 +1571,7 @@ test("Can't assign students (already used)", async () => {
reason: "This student does already have a contract",
});
expectCall(reqMock.parseDraftStudentRequest, req);
expectCall(utilMock.isAdmin, req.body);
expectCall(utilMock.checkSessionKey, req.body);
expect(ormOMock.getLatestOsoc).toHaveBeenCalledTimes(1);
expectCall(ormCMock.contractsForStudent, req.body.studentId);
expect(ormCMock.createContract).not.toHaveBeenCalled();
Expand Down
3 changes: 3 additions & 0 deletions backend/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,7 @@ export interface ServerToClientEvents {
projectWasCreatedOrDeleted: () => void;
projectWasModified: (projectId: number) => void;
osocWasCreatedOrDeleted: () => void;
yearPermissionUpdated: (loginUserId: number) => void;
}

/**
Expand All @@ -1547,6 +1548,7 @@ export interface ClientToServerEvents {
projectDeleted: () => void;
osocDeleted: () => void;
osocCreated: () => void;
yearPermissionUpdate: (loginUserId: number) => void;
}

/**
Expand All @@ -1567,6 +1569,7 @@ export enum Decision {
YES = "YES",
MAYBE = "MAYBE",
NO = "NO",
NONE = "NONE",
}

export enum AccountStatus {
Expand Down
24 changes: 24 additions & 0 deletions backend/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { getAppliedYearsForStudent } from "./orm_functions/student";
import IdRequest = Requests.IdRequest;
import { getProjectYear } from "./orm_functions/project";
import { getOsocById } from "./orm_functions/osoc";
import { getJobApplication } from "./orm_functions/job_application";

/**
* The API error cooking functions. HTTP error codes are loaded from
Expand Down Expand Up @@ -416,6 +417,29 @@ export async function checkYearPermissionOsoc<T extends IdRequest>(
return Promise.reject(errors.cookInsufficientRights());
}

/**
* returns the userData object if the user is allowed to see the followup
* Otherwise it returns an insufficient rights error.
* @param userData: object with the userId and osocID
*/
export async function checkYearPermissionsFollowup<T extends IdRequest>(
userData: WithUserID<T>
): Promise<WithUserID<T>> {
// get the years that are visible for the loginUser
const visibleYears = await getOsocYearsForLoginUser(userData.userId);
// get the year that the application belongs to
const job_application = await getJobApplication(userData.data.id);

// check if the project year is inside the visible years for the user
if (
job_application !== null &&
visibleYears.includes(job_application.osoc.year)
) {
return userData;
}
return Promise.reject(errors.cookInsufficientRights());
}

/**
* Generates a new session key.
* @returns The newly generated session key.
Expand Down
4 changes: 4 additions & 0 deletions backend/websocket_events/osoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export function registerOsocHandlers(
const OsocCreatedOrDeleted = () => {
socket.broadcast.emit("osocWasCreatedOrDeleted");
};
const yearPermissionUpdated = (loginUserId: number) => {
socket.broadcast.emit("yearPermissionUpdated", loginUserId);
};

socket.on("osocDeleted", OsocCreatedOrDeleted);
socket.on("osocCreated", OsocCreatedOrDeleted);
socket.on("yearPermissionUpdate", yearPermissionUpdated);
}
Loading

2 comments on commit bc22a67

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report for frontend

St.❔
Category Percentage Covered / Total
🟑 Statements 67.86% 1427/2103
πŸ”΄ Branches 50.28% 453/901
πŸ”΄ Functions 56.39% 234/415
🟑 Lines 67.18% 1349/2008

Test suite run success

39 tests passing in 9 suites.

Report generated by πŸ§ͺjest coverage report action from bc22a67

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report for backend

St.❔
Category Percentage Covered / Total
🟒 Statements 98.9% 2528/2556
🟒 Branches 96.86% 678/700
🟒 Functions 98.02% 694/708
🟒 Lines 99% 2485/2510

Test suite run success

3887 tests passing in 87 suites.

Report generated by πŸ§ͺjest coverage report action from bc22a67

Please sign in to comment.