update job application

This commit is contained in:
xfarrow 2024-10-24 17:23:09 +02:00
parent 18d1babe33
commit 3d4299da6f
4 changed files with 111 additions and 20 deletions

View File

@ -11,6 +11,7 @@
IN THE SOFTWARE. IN THE SOFTWARE.
*/ */
const knex = require('../utils/knex_config'); const knex = require('../utils/knex_config');
const OrganizationAdmin = require('../models/organization_admin_model');
/** /**
* Inserts a new JobApplication. * Inserts a new JobApplication.
@ -99,7 +100,43 @@ async function getApplicansByOrganization(organizationId) {
description: applicant.description, description: applicant.description,
}, },
})); }));
}
async function find(jobApplicationId) {
return await knex('JobApplication')
.where('id', jobApplicationId)
.first();
}
async function remove(jobApplicationId) {
return await knex('JobApplication')
.where('id', jobApplicationId)
.del();
}
async function setStatus(jobApplicationId, status) {
return await knex('JobApplication')
.where('id', jobApplicationId)
.update('application_status', status);
}
/**
* Only Organization Administrators can change the status of a JobApplication
* @param {*} jobApplicationId
* @param {*} personId
*/
async function canPersonSetStatus(jobApplicationId, personId) {
const organization = await knex('JobApplication')
.where('JobApplication.id', jobApplicationId)
.join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id')
.join('Organization', 'Organization.id', 'JobOffer.organization_id')
.first()
.select('Organization.id');
if (organization == null) {
return false;
}
const isAdmin = await OrganizationAdmin.isAdmin(personId, organization.id);
return isAdmin;
} }
module.exports = { module.exports = {
@ -107,5 +144,9 @@ module.exports = {
userAlreadyApplicated, userAlreadyApplicated,
getMyApplications, getMyApplications,
getApplicantsByJobOffer, getApplicantsByJobOffer,
getApplicansByOrganization getApplicansByOrganization,
find,
remove,
setStatus,
canPersonSetStatus
} }

View File

@ -1,3 +1,5 @@
// TODO: I don't like the routes. It should be /api/organizations/idOrganization/joboffers/idJobOffer/
// TODO: Create a validator
/* /*
This code is part of Blink This code is part of Blink
licensed under GPLv3 licensed under GPLv3
@ -18,12 +20,9 @@ const express = require('express');
const jwtUtils = require('../utils/jwt_utils'); const jwtUtils = require('../utils/jwt_utils');
/** /**
* POST Request * **POST** Request
* *
* Inserts a new job application * Inserts a new job application
* @param {*} req
* @param {*} res
* @returns
*/ */
async function insert(req, res) { async function insert(req, res) {
try { try {
@ -53,12 +52,9 @@ async function insert(req, res) {
} }
/** /**
* GET Request * **GET** Request
* *
* Retrieves all the job applications of the logged in user * Retrieves all the job applications of the logged in user
* @param {*} req
* @param {*} res
* @returns
*/ */
async function myApplications(req, res) { async function myApplications(req, res) {
try { try {
@ -73,15 +69,15 @@ async function myApplications(req, res) {
} }
/** /**
* GET Request. Retrieve all the applicants who applicated to a job offer. * **GET** Request
*
* Retrieve all the applicants who applicated to a job offer.
* Only an organization administrator is allowed to perform this action. * Only an organization administrator is allowed to perform this action.
* @param {*} req
* @param {*} res
*/ */
async function getApplicantsByJobOffer(req, res) { async function getApplicantsByJobOffer(req, res) {
try { try {
const isAdmin = await OrganizationAdmin.isAdmin(req.jwt.person_id, jobOffer.organization_id); const isAdmin = await OrganizationAdmin.isAdmin(req.jwt.person_id, req.body.organizationId);
if(!isAdmin){ if (!isAdmin) {
return res.status(401).json({ return res.status(401).json({
error: 'Forbidden' error: 'Forbidden'
}); });
@ -97,16 +93,16 @@ async function getApplicantsByJobOffer(req, res) {
} }
/** /**
* GET Request. Retrieve all the applicants who applicated to a job offer created * **GET** Request
*
* Retrieve all the applicants who applicated to a job offer created
* by the specific organization. * by the specific organization.
* Only an organization administrator is allowed to perform this action. * Only an organization administrator is allowed to perform this action.
* @param {*} req
* @param {*} res
*/ */
async function getApplicantsByOrganization(req, res){ async function getApplicantsByOrganization(req, res) {
try { try {
const isAdmin = await OrganizationAdmin.isAdmin(req.jwt.person_id, req.body.organizationId); const isAdmin = await OrganizationAdmin.isAdmin(req.jwt.person_id, req.body.organizationId);
if(!isAdmin){ if (!isAdmin) {
return res.status(401).json({ return res.status(401).json({
error: 'Forbidden' error: 'Forbidden'
}); });
@ -121,11 +117,63 @@ async function getApplicantsByOrganization(req, res){
} }
} }
/**
* **DELETE** Request
*
* Removes a job application. Only the applicant is allowed to do that.
*/
async function remove(req, res) {
try {
const jobApplication = await Application.find(req.body.jobApplicationId);
if (jobApplication == null) {
return res.status(404).send();
}
if (jobApplication.person_id !== req.jwt.person_id) {
return res.status(401).json({
error: 'Forbidden'
});
}
await Application.remove(req.body.jobApplicationId);
return res.status(200).send();
} catch (error) {
console.error(`Error in function ${remove.name}: ${error}`);
res.status(500).json({
error: 'Internal server error'
});
}
}
/**
* **PATCH** Request
*
* Sets a new status to a JobApplication. Only an Organization Administrator
* can perform this action
*/
async function setStatus(req, res) {
try {
const canPersonSetStatus = Application.canPersonSetStatus(req.body.jobApplication, req.jwt.person_id);
if (!canPersonSetStatus) {
return res.status(401).json({
error: 'Forbidden'
});
}
await Application.setStatus(req.body.jobApplication, req.body.status);
return res.status(204).send();
} catch (error) {
console.error(`Error in function ${remove.name}: ${error}`);
res.status(500).json({
error: 'Internal server error'
});
}
}
const routes = express.Router(); const routes = express.Router();
routes.post('/', jwtUtils.extractToken, insert); routes.post('/', jwtUtils.extractToken, insert);
routes.get('/myapplications', jwtUtils.extractToken, myApplications); routes.get('/myapplications', jwtUtils.extractToken, myApplications);
routes.get('/applicantsbyjoboffer', jwtUtils.extractToken, getApplicantsByJobOffer); routes.get('/applicantsbyjoboffer', jwtUtils.extractToken, getApplicantsByJobOffer);
routes.get('/applicantsbyorganization', jwtUtils.extractToken, getApplicantsByOrganization); routes.get('/applicantsbyorganization', jwtUtils.extractToken, getApplicantsByOrganization);
routes.delete('/', jwtUtils.extractToken, remove);
routes.patch('/', jwtUtils.extractToken, setStatus);
module.exports = { module.exports = {
routes routes

View File

@ -5,6 +5,7 @@ CREATE TYPE ContractType as ENUM ('FULL-TIME','PART-TIME','INTERNSHIP','CONTRACT
CREATE TYPE ExperienceType as ENUM ('EDUCATION', 'WORK', 'VOLOUNTEER'); CREATE TYPE ExperienceType as ENUM ('EDUCATION', 'WORK', 'VOLOUNTEER');
CREATE TYPE SalaryFrequency as ENUM ('HOURLY', 'WEEKLY', 'YEARLY'); CREATE TYPE SalaryFrequency as ENUM ('HOURLY', 'WEEKLY', 'YEARLY');
CREATE TYPE InfoType AS ENUM ('EMAIL', 'PHONE', 'WEBSITE', 'GIT', 'MASTODON', 'YOUTUBE', 'FACEBOOK', 'INSTAGRAM', 'OTHER'); CREATE TYPE InfoType AS ENUM ('EMAIL', 'PHONE', 'WEBSITE', 'GIT', 'MASTODON', 'YOUTUBE', 'FACEBOOK', 'INSTAGRAM', 'OTHER');
CREATE TYPE ApplicationStatus AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');
-- Table: Person -- Table: Person
CREATE TABLE IF NOT EXISTS "Person" ( CREATE TABLE IF NOT EXISTS "Person" (
@ -81,6 +82,7 @@ CREATE TABLE IF NOT EXISTS "JobApplication" (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
person_id INTEGER NOT NULL, person_id INTEGER NOT NULL,
job_offer_id INTEGER NOT NULL, job_offer_id INTEGER NOT NULL,
application_status ApplicationStatus NOT NULL DEFAULT 'PENDING',
CONSTRAINT "PersonFk" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE, CONSTRAINT "PersonFk" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT "JobOfferFk" FOREIGN KEY (job_offer_id) REFERENCES "JobOffer" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE CONSTRAINT "JobOfferFk" FOREIGN KEY (job_offer_id) REFERENCES "JobOffer" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
); );

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB