This commit is contained in:
xfarrow 2024-10-30 15:17:30 +01:00
parent f8ce8064c2
commit aa06d64341
4 changed files with 127 additions and 15 deletions

View File

@ -55,13 +55,25 @@ async function getMyApplications(personId, organizationId) {
.where('person_id', personId) .where('person_id', personId)
.join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id') .join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id')
.join('Organization', 'Organization.id', 'JobOffer.organization_id') .join('Organization', 'Organization.id', 'JobOffer.organization_id')
.select('JobApplication.id', 'JobOffer.title', 'JobOffer.description', 'Organization.name', 'Organization.location'); .select('JobApplication.id', 'JobOffer.id AS JobOfferId', 'JobOffer.title', 'JobOffer.description', 'Organization.id AS OrganizationId', 'Organization.name', 'Organization.location');
if (organizationId != null) { if (organizationId != null) {
query.where('Organization.id', organizationId); query.where('Organization.id', organizationId);
} }
return await query; return (await query).map(myApplication => ({
id: myApplication.id,
JobOffer: {
id: myApplication.JobOfferId,
title: myApplication.title,
description: myApplication.description
},
Organization: {
id: myApplication.OrganizationId,
name: myApplication.name,
location: myApplication.location
}
}));
} }
/** /**
@ -85,7 +97,7 @@ async function getApplicantsByJobOffer(jobOfferId) {
* @param {*} organizationId * @param {*} organizationId
* @returns An array of Person and JobOffer objects. Throws an exception if an error occurs * @returns An array of Person and JobOffer objects. Throws an exception if an error occurs
*/ */
async function getApplicansByOrganization(organizationId) { async function getApplicantsByOrganization(organizationId) {
const applicants = await knex('JobApplication') const applicants = await knex('JobApplication')
.join('Person', 'Person.id', 'JobApplication.person_id') .join('Person', 'Person.id', 'JobApplication.person_id')
.join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id') .join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id')
@ -127,11 +139,12 @@ async function setStatus(jobApplicationId, status) {
} }
/** /**
* Only Organization Administrators can change the status of a JobApplication * Determines whether the submitted personId represents an administrator
* of the company which created the joboffer of the current application
* @param {*} jobApplicationId * @param {*} jobApplicationId
* @param {*} personId * @param {*} personId
*/ */
async function canPersonSetStatus(jobApplicationId, personId) { async function isPersonJobApplicationAdministrator(jobApplicationId, personId) {
const organization = await knex('JobApplication') const organization = await knex('JobApplication')
.where('JobApplication.id', jobApplicationId) .where('JobApplication.id', jobApplicationId)
.join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id') .join('JobOffer', 'JobOffer.id', 'JobApplication.job_offer_id')
@ -150,9 +163,9 @@ module.exports = {
userAlreadyApplicated, userAlreadyApplicated,
getMyApplications, getMyApplications,
getApplicantsByJobOffer, getApplicantsByJobOffer,
getApplicansByOrganization, getApplicantsByOrganization,
find, find,
remove, remove,
setStatus, setStatus,
canPersonSetStatus isPersonJobApplicationAdministrator
} }

View File

@ -97,6 +97,19 @@ async function findByOrganizationId(organizationId) {
return result; return result;
} }
async function isPersonJobOfferAdministrator(personId, jobOfferId) {
const organization = await knex('JobOffer')
.where('JobOffer.id', jobOfferId)
.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;
}
// test // test
async function filter(title, description, requirements, salary, salaryOperator, salaryFrequency, location, tags) { async function filter(title, description, requirements, salary, salaryOperator, salaryFrequency, location, tags) {
let query = knex('JobOffer'); let query = knex('JobOffer');
@ -132,5 +145,6 @@ module.exports = {
insert, insert,
remove, remove,
findByOrganizationId, findByOrganizationId,
findById findById,
isPersonJobOfferAdministrator
} }

View File

@ -17,6 +17,7 @@ const JobOffer = require('../models/job_offer_model');
const OrganizationAdmin = require('../models/organization_admin_model'); const OrganizationAdmin = require('../models/organization_admin_model');
const express = require('express'); const express = require('express');
const jwtUtils = require('../utils/jwt_utils'); const jwtUtils = require('../utils/jwt_utils');
const jobApplicationValidator = require('../utils/validators/job_application_validator');
/** /**
* **POST** Request * **POST** Request
@ -25,6 +26,13 @@ const jwtUtils = require('../utils/jwt_utils');
*/ */
async function insert(req, res) { async function insert(req, res) {
try { try {
const errors = jobApplicationValidator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
// Check if the job offer exists // Check if the job offer exists
if (await JobOffer.findById(req.params.idJobOffer) == null) { if (await JobOffer.findById(req.params.idJobOffer) == null) {
return res.status(404).json({ return res.status(404).json({
@ -50,7 +58,6 @@ async function insert(req, res) {
} }
} }
// TODO
/** /**
* **GET** Request * **GET** Request
* *
@ -60,13 +67,32 @@ async function insert(req, res) {
* @param {*} req * @param {*} req
* @param {*} res * @param {*} res
*/ */
async function find(req, res){ async function find(req, res) {
try { try {
const errors = jobApplicationValidator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const jobApplication = await Application.find(req.params.idApplication); const jobApplication = await Application.find(req.params.idApplication);
if (jobApplication == null) { if (jobApplication == null) {
return res.status(404).send(); return res.status(404).send();
} }
// Case in which the user themselves requested it
if (jobApplication.person_id == req.jwt.person_id) {
return res.status(200).json(jobApplication)
} else {
const isPersonOrganizationAdmin = await Application.isPersonJobApplicationAdministrator(jobApplication.id, req.jwt.person_id);
if (isPersonOrganizationAdmin === true) {
return res.status(200).json(jobApplication)
} else {
return res.status(401).json({
error: 'Forbidden'
});
}
}
} catch (error) { } catch (error) {
console.error(`Error in function ${find.name}: ${error}`); console.error(`Error in function ${find.name}: ${error}`);
res.status(500).json({ res.status(500).json({
@ -82,6 +108,13 @@ async function find(req, res){
*/ */
async function myApplications(req, res) { async function myApplications(req, res) {
try { try {
const errors = jobApplicationValidator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const applications = await Application.getMyApplications(req.jwt.person_id, req.body.organizationId); const applications = await Application.getMyApplications(req.jwt.person_id, req.body.organizationId);
return res.status(200).json(applications); return res.status(200).json(applications);
} catch (error) { } catch (error) {
@ -100,7 +133,14 @@ async function myApplications(req, res) {
*/ */
async function getApplicationsByJobOffer(req, res) { async function getApplicationsByJobOffer(req, res) {
try { try {
const isAdmin = await OrganizationAdmin.isAdmin(req.jwt.person_id, req.params.idJobOffer); //todo error! It's not idJobOffer const errors = jobApplicationValidator.validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const isAdmin = await JobOffer.isPersonJobOfferAdministrator(req.jwt.person_id, req.params.idJobOffer);
if (!isAdmin) { if (!isAdmin) {
return res.status(401).json({ return res.status(401).json({
error: 'Forbidden' error: 'Forbidden'
@ -131,7 +171,7 @@ async function getApplicationsByOrganization(req, res) {
error: 'Forbidden' error: 'Forbidden'
}); });
} }
const applicants = await Application.getApplicansByOrganization(req.params.idOrganization); const applicants = await Application.getApplicantsByOrganization(req.params.idOrganization);
return res.status(200).json(applicants); return res.status(200).json(applicants);
} catch (error) { } catch (error) {
console.error(`Error in function ${getApplicationsByOrganization.name}: ${error}`); console.error(`Error in function ${getApplicationsByOrganization.name}: ${error}`);
@ -175,7 +215,7 @@ async function remove(req, res) {
*/ */
async function setStatus(req, res) { async function setStatus(req, res) {
try { try {
const canPersonSetStatus = await Application.canPersonSetStatus(req.params.idApplication, req.jwt.person_id); const canPersonSetStatus = await Application.isPersonJobApplicationAdministrator(req.params.idApplication, req.jwt.person_id);
if (!canPersonSetStatus) { if (!canPersonSetStatus) {
return res.status(401).json({ return res.status(401).json({
error: 'Forbidden' error: 'Forbidden'
@ -192,7 +232,7 @@ async function setStatus(req, res) {
} }
const routes = express.Router(); const routes = express.Router();
routes.post('/joboffers/:idJobOffer/applications', jwtUtils.extractToken, insert); routes.post('/joboffers/:idJobOffer/applications', jwtUtils.extractToken, jobApplicationValidator.insertValidator, insert);
routes.get('/joboffers/:idJobOffer/applications/:idApplication', jwtUtils.extractToken, find); routes.get('/joboffers/:idJobOffer/applications/:idApplication', jwtUtils.extractToken, find);
routes.get('/joboffers/applications/mine', jwtUtils.extractToken, myApplications); routes.get('/joboffers/applications/mine', jwtUtils.extractToken, myApplications);
routes.get('/joboffers/:idJobOffer/applications', jwtUtils.extractToken, getApplicationsByJobOffer); routes.get('/joboffers/:idJobOffer/applications', jwtUtils.extractToken, getApplicationsByJobOffer);

View File

@ -0,0 +1,45 @@
/*
This code is part of Blink
licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
const {
check,
validationResult
} = require("express-validator"); // This is the destructuring part. It specifies which properties of the imported object (express-validator) you want to extract.
const {
escape
} = require('validator');
const insertValidator = [
check('idJobOffer').trim().escape().isInt()
];
const findValidator = [
check('idApplication').trim().escape().isInt()
];
const myApplicationsValidator = [
check('organizationId').optional().trim().escape().isInt()
];
const getApplicationsByJobOfferValidator = [
check('idJobOffer').optional().trim().escape().isInt()
];
module.exports = {
insertValidator,
findValidator,
myApplicationsValidator,
getApplicationsByJobOfferValidator,
validationResult
}