npx eslint --fix

This commit is contained in:
Alessandro Ferro
2024-02-23 11:17:02 +01:00
parent 4a712f4de3
commit 7bd6889768
18 changed files with 618 additions and 662 deletions

View File

@ -20,10 +20,9 @@ const rateLimit = require('express-rate-limit');
const personRoutes = require('./routes/person_routes.js'); const personRoutes = require('./routes/person_routes.js');
const organizationRoutes = require('./routes/organization_routes.js'); const organizationRoutes = require('./routes/organization_routes.js');
const organizationAdminRoutes = require('./routes/organization_admin_routes.js'); const organizationAdminRoutes = require('./routes/organization_admin_routes.js');
const organizationPostRoutes = require('./routes/organization_post_routes.js') const organizationPostRoutes = require('./routes/organization_post_routes.js');
const jwt_utils = require('./utils/jwt_utils.js'); const jwt_utils = require('./utils/jwt_utils.js');
// Application configuration // Application configuration
const app = express(); const app = express();
app.use(express.json()); // Middleware which parses JSON for POST requests app.use(express.json()); // Middleware which parses JSON for POST requests
@ -31,7 +30,7 @@ app.use(cors()); // Enable CORS for all routes
app.use(rateLimit({ app.use(rateLimit({
windowMs: process.env.LIMITER_WINDOW, windowMs: process.env.LIMITER_WINDOW,
max: process.env.LIMITER_MAXIMUM_PER_WINDOW, max: process.env.LIMITER_MAXIMUM_PER_WINDOW,
message: {error : "Too many requests from this IP, please try again later"} message: { error: 'Too many requests from this IP, please try again later' }
})); // Apply the rate limiter middleware to all routes })); // Apply the rate limiter middleware to all routes
const publicRoutes = express.Router(); const publicRoutes = express.Router();

View File

@ -20,13 +20,13 @@ const knex = require('../utils/knex_config');
* @param {*} organizationId * @param {*} organizationId
* @returns true if administrator, false otherwise * @returns true if administrator, false otherwise
*/ */
async function isPersonAdmin(personId, organizationId){ async function isPersonAdmin (personId, organizationId) {
const isPersonAdmin = await knex('OrganizationAdministrator') const isPersonAdmin = await knex('OrganizationAdministrator')
.where('id_person', personId) .where('id_person', personId)
.where('id_organization', organizationId) .where('id_organization', organizationId)
.select('*') .select('*')
.first(); .first();
return isPersonAdmin; return isPersonAdmin;
} }
/** /**
@ -34,12 +34,12 @@ async function isPersonAdmin(personId, organizationId){
* @param {*} personId * @param {*} personId
* @param {*} organizationId * @param {*} organizationId
*/ */
async function addOrganizationAdministrator(personId, organizationId){ async function addOrganizationAdministrator (personId, organizationId) {
await knex('OrganizationAdministrator') await knex('OrganizationAdministrator')
.insert({ .insert({
id_person: personId, id_person: personId,
id_organization: organizationId id_organization: organizationId
}); });
} }
/** /**
@ -48,35 +48,35 @@ async function addOrganizationAdministrator(personId, organizationId){
* @param {*} personId * @param {*} personId
* @param {*} organizationId * @param {*} organizationId
*/ */
async function removeOrganizationAdmin(personId, organizationId){ async function removeOrganizationAdmin (personId, organizationId) {
const transaction = await knex.transaction(); const transaction = await knex.transaction();
// We lock the table to ensure that we won't have concurrency issues // We lock the table to ensure that we won't have concurrency issues
// while checking remainingAdministrators. // while checking remainingAdministrators.
// TODO: Understand whether a lock on the table is really necessary // TODO: Understand whether a lock on the table is really necessary
await transaction.raw('LOCK TABLE "OrganizationAdministrator" IN SHARE MODE'); await transaction.raw('LOCK TABLE "OrganizationAdministrator" IN SHARE MODE');
await transaction('OrganizationAdministrator') await transaction('OrganizationAdministrator')
.where('id_person', personId) .where('id_person', personId)
.where('id_organization', organizationId) .where('id_organization', organizationId)
.del();
// TODO: If the user instead deletes their entire profile, the organization will not be deleted. Fix. (database schema)
const remainingAdministrators = await transaction('OrganizationAdministrator')
.where({ id_organization: organizationId });
if (remainingAdministrators.length === 0) {
// If no more users, delete the organization
await transaction('Organization')
.where('id', organizationId)
.del(); .del();
}
// TODO: If the user instead deletes their entire profile, the organization will not be deleted. Fix. (database schema) await transaction.commit();
const remainingAdministrators = await transaction('OrganizationAdministrator')
.where({ id_organization: organizationId });
if (remainingAdministrators.length === 0) {
// If no more users, delete the organization
await transaction('Organization')
.where('id', organizationId)
.del();
}
await transaction.commit();
} }
module.exports = { module.exports = {
isPersonAdmin, isPersonAdmin,
addOrganizationAdministrator, addOrganizationAdministrator,
removeOrganizationAdmin removeOrganizationAdmin
}; };

View File

@ -21,14 +21,14 @@ const knex = require('../utils/knex_config');
* @param {*} is_hiring * @param {*} is_hiring
* @returns * @returns
*/ */
function organization(name, location, description, is_hiring){ function organization (name, location, description, is_hiring) {
const organization = { const organization = {
name: name, name,
location: location, location,
description: description, description,
is_hiring: is_hiring is_hiring
}; };
return organization; return organization;
} }
/** /**
@ -36,7 +36,7 @@ function organization(name, location, description, is_hiring){
* @param {*} id * @param {*} id
* @returns * @returns
*/ */
async function getOrganizationById(id){ async function getOrganizationById (id) {
const organization = await knex('Organization') const organization = await knex('Organization')
.where('id', id) .where('id', id)
.select('*') .select('*')
@ -48,19 +48,19 @@ async function getOrganizationById(id){
* Insert an Organization and its relative Administrator * Insert an Organization and its relative Administrator
* @param {*} organization * @param {*} organization
*/ */
async function insertOrganization(organization, organizationAdministratorId){ async function insertOrganization (organization, organizationAdministratorId) {
await knex.transaction(async (trx) => { await knex.transaction(async (trx) => {
// We have to insert either both in Organization and in OrganizationAdministrator // We have to insert either both in Organization and in OrganizationAdministrator
// or in neither // or in neither
const organizationResult = await trx('Organization') const organizationResult = await trx('Organization')
.insert(organization, '*'); .insert(organization, '*');
// Inserting in the "OrganizationAdministrator" table // Inserting in the "OrganizationAdministrator" table
await trx('OrganizationAdministrator') await trx('OrganizationAdministrator')
.insert({ .insert({
id_person: organizationAdministratorId, id_person: organizationAdministratorId,
id_organization: organizationResult[0].id, id_organization: organizationResult[0].id
}); });
}); });
} }
@ -72,43 +72,43 @@ async function insertOrganization(organization, organizationAdministratorId){
* @param {*} personId * @param {*} personId
* @returns true if the row was updated, false otherwise * @returns true if the row was updated, false otherwise
*/ */
async function updateOrganizationIfAdministrator(organization, organizationId, personId){ async function updateOrganizationIfAdministrator (organization, organizationId, personId) {
// // const isOrganizationAdmin = await knex('OrganizationAdministrator') // // const isOrganizationAdmin = await knex('OrganizationAdministrator')
// // .where('id_person', req.jwt.person_id) // // .where('id_person', req.jwt.person_id)
// // .where('id_organization', req.params.id) // // .where('id_organization', req.params.id)
// // .select('*') // // .select('*')
// // .first(); // // .first();
// // // This introduces a Time of check Time of use weakeness // // // This introduces a Time of check Time of use weakeness
// // // which could'have been fixed by either // // // which could'have been fixed by either
// // // 1) Using "whereExists", thanks to the "it's easier to ask for // // // 1) Using "whereExists", thanks to the "it's easier to ask for
// // // forgiveness than for permission" padarigm. Or, // // // forgiveness than for permission" padarigm. Or,
// // // 2) Using a serializable transaction. // // // 2) Using a serializable transaction.
// // // // // //
// // // The undersigned chose not to follow these approaches because // // // The undersigned chose not to follow these approaches because
// // // this does not introduces any serious vulnerability. In this // // // this does not introduces any serious vulnerability. In this
// // // way it seems more readable. // // // way it seems more readable.
// // if(!isOrganizationAdmin){ // // if(!isOrganizationAdmin){
// // return res.status(403).json({error : "Forbidden"}); // // return res.status(403).json({error : "Forbidden"});
// // } // // }
// // await knex('Organization') // // await knex('Organization')
// // .where('id', req.params.id) // // .where('id', req.params.id)
// // .update({ // // .update({
// // name: req.body.name, // // name: req.body.name,
// // location: req.body.location, // // location: req.body.location,
// // description: req.body.description, // // description: req.body.description,
// // is_hiring: req.body.is_hiring // // is_hiring: req.body.is_hiring
// // }); // // });
const numberOfUpdatedRows = await knex('Organization') const numberOfUpdatedRows = await knex('Organization')
.where('id', organizationId) .where('id', organizationId)
.whereExists(function(){ .whereExists(function () {
this.select('*') this.select('*')
.from('OrganizationAdministrator') .from('OrganizationAdministrator')
.where('id_person', personId) .where('id_person', personId)
.where('id_organization', organizationId) .where('id_organization', organizationId);
}) })
.update(organization); .update(organization);
return numberOfUpdatedRows == 1; return numberOfUpdatedRows == 1;
@ -121,14 +121,14 @@ async function updateOrganizationIfAdministrator(organization, organizationId, p
* @param {*} personId PersonId of the supposedly administrator * @param {*} personId PersonId of the supposedly administrator
* @returns true if the Organization was successfully deleted, false otherwise * @returns true if the Organization was successfully deleted, false otherwise
*/ */
async function deleteOrganizationIfAdmin(organizationId, personId){ async function deleteOrganizationIfAdmin (organizationId, personId) {
const numberOfDeletedRows = await knex('Organization') const numberOfDeletedRows = await knex('Organization')
.where({ id: organizationId }) .where({ id: organizationId })
.whereExists(function(){ .whereExists(function () {
this.select('*') this.select('*')
.from('OrganizationAdministrator') .from('OrganizationAdministrator')
.where('id_person', personId) .where('id_person', personId)
.where('id_organization', organizationId) .where('id_organization', organizationId);
}) })
.del(); .del();
return numberOfDeletedRows == 1; return numberOfDeletedRows == 1;

View File

@ -25,17 +25,17 @@ const bcrypt = require('bcrypt');
* @param {*} place_of_living * @param {*} place_of_living
* @returns * @returns
*/ */
function person(email, password, display_name, date_of_birth, available, enabled, place_of_living) { function person (email, password, display_name, date_of_birth, available, enabled, place_of_living) {
const person = { const person = {
email: email.toLowerCase(), email: email.toLowerCase(),
password: password, password,
display_name: display_name, display_name,
date_of_birth: date_of_birth, date_of_birth,
available: available, available,
enabled: enabled, enabled,
place_of_living: place_of_living place_of_living
}; };
return person; return person;
} }
/** /**
@ -43,10 +43,10 @@ function person(email, password, display_name, date_of_birth, available, enabled
* @param {*} email email to look the Person for * @param {*} email email to look the Person for
* @returns the Person object * @returns the Person object
*/ */
async function getPersonByEmail(email){ async function getPersonByEmail (email) {
return await knex('Person') return await knex('Person')
.where('email', email.toLowerCase()) .where('email', email.toLowerCase())
.first(); .first();
} }
/** /**
@ -54,11 +54,11 @@ async function getPersonByEmail(email){
* @param {*} id - The id to look the person for * @param {*} id - The id to look the person for
* @returns * @returns
*/ */
async function getPersonById(id){ async function getPersonById (id) {
return await knex('Person') return await knex('Person')
.select('*') .select('*')
.where({ id: id }) .where({ id })
.first(); .first();
} }
/** /**
@ -67,27 +67,27 @@ async function getPersonById(id){
* @param {*} person A Person object * @param {*} person A Person object
* @param {*} activationLink the activationLink identifier * @param {*} activationLink the activationLink identifier
*/ */
async function registerPerson(person, activationLink){ async function registerPerson (person, activationLink) {
// We need to insert either both in the "Person" table // We need to insert either both in the "Person" table
// and in the "ActivationLink" one, or in neither // and in the "ActivationLink" one, or in neither
await knex.transaction(async (tr) => { await knex.transaction(async (tr) => {
const personIdResult = await tr('Person') const personIdResult = await tr('Person')
.insert({ .insert({
email: person.email.toLowerCase(), email: person.email.toLowerCase(),
password: person.password, password: person.password,
display_name: person.display_name, display_name: person.display_name,
date_of_birth: person.date_of_birth, date_of_birth: person.date_of_birth,
available: person.available, available: person.available,
enabled: person.enabled, enabled: person.enabled,
place_of_living: person.place_of_living place_of_living: person.place_of_living
}) })
.returning("id"); .returning('id');
await tr('ActivationLink') await tr('ActivationLink')
.insert({ .insert({
person_id: personIdResult[0].id, person_id: personIdResult[0].id,
identifier: activationLink identifier: activationLink
}); });
}); });
} }
/** /**
@ -97,20 +97,20 @@ async function registerPerson(person, activationLink){
* @param {*} password * @param {*} password
* @returns * @returns
*/ */
async function getPersonByEmailAndPassword(email, password){ async function getPersonByEmailAndPassword (email, password) {
const person = await knex('Person') const person = await knex('Person')
.where('email', email.toLowerCase()) .where('email', email.toLowerCase())
.where('enabled', true) .where('enabled', true)
.select('*') .select('*')
.first(); .first();
if(person){ if (person) {
const passwordMatches = await bcrypt.compare(password, person.password); const passwordMatches = await bcrypt.compare(password, person.password);
if (passwordMatches) { if (passwordMatches) {
return person; return person;
}
} }
return null; }
return null;
} }
/** /**
@ -118,32 +118,31 @@ async function getPersonByEmailAndPassword(email, password){
* @param {*} person The Person to update * @param {*} person The Person to update
* @param {*} person_id The database id of the Person to update * @param {*} person_id The database id of the Person to update
*/ */
async function updatePerson(person, person_id){ async function updatePerson (person, person_id) {
await knex('Person') await knex('Person')
.where('id', person_id) .where('id', person_id)
.update(person); .update(person);
} }
/** /**
* Deletes a Person specified by its database id. * Deletes a Person specified by its database id.
* @param {*} person_id * @param {*} person_id
*/ */
async function deletePerson(person_id){ async function deletePerson (person_id) {
await knex('Person') await knex('Person')
.where({id : person_id}) .where({ id: person_id })
.del(); .del();
} }
// Exporting a function // Exporting a function
// means making a JavaScript function defined in one // means making a JavaScript function defined in one
// module available for use in another module. // module available for use in another module.
module.exports = { module.exports = {
person, person,
getPersonByEmail, getPersonByEmail,
getPersonById, getPersonById,
getPersonByEmailAndPassword, getPersonByEmailAndPassword,
registerPerson, registerPerson,
updatePerson, updatePerson,
deletePerson deletePerson
}; };

View File

@ -22,29 +22,27 @@ const organization_admin_model = require('../models/organization_admin_model');
* *
* Required field(s): organization_id, person_id * Required field(s): organization_id, person_id
*/ */
async function addOrganizationAdmin(req, res){ async function addOrganizationAdmin (req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.person_id) {
return res.status(400).json({ error: 'Invalid request' });
}
// Ensure that the required fields are present before proceeding try {
if (!req.body.organization_id || !req.body.person_id) { const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id);
return res.status(400).json({ error : "Invalid request"}); // TOC/TOU
} if (!isPersonAdmin) {
return res.status(401).json({ error: 'Forbidden' });
try {
const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id);
// TOC/TOU
if(!isPersonAdmin){
return res.status(401).json({error : "Forbidden"});
}
await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id);
return res.status(200).json({success : true});
}
catch (error) {
console.error('Error while adding organization admin: ' + error);
res.status(500).json({error : "Internal server error"});
} }
await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id);
return res.status(200).json({ success: true });
} catch (error) {
console.error('Error while adding organization admin: ' + error);
res.status(500).json({ error: 'Internal server error' });
}
} }
/** /**
* DELETE Request * DELETE Request
* *
* Deletes a Person from the list of Administrators of an Organization. * Deletes a Person from the list of Administrators of an Organization.
@ -53,21 +51,19 @@ async function addOrganizationAdmin(req, res){
* *
* Required field(s): organization_id * Required field(s): organization_id
*/ */
async function removeOrganizationAdmin(req, res){ async function removeOrganizationAdmin (req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id) {
return res.status(400).json({ error: 'Invalid request' });
}
// Ensure that the required fields are present before proceeding try {
if (!req.body.organization_id) { await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id);
return res.status(400).json({ error : "Invalid request"}); return res.status(200).json({ success: true });
} } catch (error) {
console.error(error);
try{ return res.status(500).json({ error: 'Internal server error' });
await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id); }
return res.status(200).json({success : true});
}
catch (error){
console.error(error);
return res.status(500).json({ error: "Internal server error"});
}
} }
module.exports = { module.exports = {

View File

@ -21,41 +21,39 @@ const knex = require('../utils/knex_config');
* Required field(s): organization_id, content * Required field(s): organization_id, content
* @returns the inserted Post * @returns the inserted Post
*/ */
async function createOrganizationPost(req, res){ async function createOrganizationPost (req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.content) {
return res.status(400).json({ error: 'Invalid request' });
}
// Ensure that the required fields are present before proceeding try {
if (!req.body.organization_id || !req.body.content) { // Check if the current user is a organization's administrator
return res.status(400).json({ error : "Invalid request"}); const isOrganizationAdmin = await knex('OrganizationAdministrator')
}
try {
// Check if the current user is a organization's administrator
const isOrganizationAdmin = await knex('OrganizationAdministrator')
.where('id_person', req.jwt.person_id) .where('id_person', req.jwt.person_id)
.where('id_organization', req.body.organization_id) .where('id_organization', req.body.organization_id)
.select('*') .select('*')
.first(); .first();
// Non-exploitable TOC/TOU weakness // Non-exploitable TOC/TOU weakness
// For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat // For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat
if(!isOrganizationAdmin){ if (!isOrganizationAdmin) {
return res.status(403).json({error : "Forbidden"}); return res.status(403).json({ error: 'Forbidden' });
} }
const organizationPost = await knex('OrganizationPost') const organizationPost = await knex('OrganizationPost')
.insert({ .insert({
organization_id: req.body.organization_id, organization_id: req.body.organization_id,
content: req.body.content, content: req.body.content,
original_author: req.jwt.person_id original_author: req.jwt.person_id
}) })
.returning('*'); .returning('*');
return res.status(200).json(organizationPost[0]); return res.status(200).json(organizationPost[0]);
} } catch (error) {
catch (error) { console.log('Error while creating Organization Post: ' + error);
console.log("Error while creating Organization Post: " + error); return res.status(500).json({ error: 'Internal server error' });
return res.status(500).json({error : "Internal server error"});
}
} }
}
/** /**
* DELETE Request * DELETE Request
@ -65,11 +63,10 @@ async function createOrganizationPost(req, res){
* *
* Required field(s): none. * Required field(s): none.
*/ */
async function deleteOrganizationPost(req, res){ async function deleteOrganizationPost (req, res) {
const organizationPostIdToDelete = req.params.id; const organizationPostIdToDelete = req.params.id;
try{ try {
const isOrganizationAdmin = await knex('OrganizationPost') const isOrganizationAdmin = await knex('OrganizationPost')
.join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization') .join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization')
.where('OrganizationPost.id', organizationPostIdToDelete) .where('OrganizationPost.id', organizationPostIdToDelete)
@ -77,20 +74,18 @@ async function deleteOrganizationPost(req, res){
.select('*') .select('*')
.first(); .first();
// Unexploitable TOC/TOU // Unexploitable TOC/TOU
if(isOrganizationAdmin){ if (isOrganizationAdmin) {
await knex('OrganizationPost') await knex('OrganizationPost')
.where('id', organizationPostIdToDelete) .where('id', organizationPostIdToDelete)
.del(); .del();
return res.status(200).json({success : true}); return res.status(200).json({ success: true });
} } else {
else{ return res.status(401).json({ error: 'Forbidden' });
return res.status(401).json({error : "Forbidden"}); }
} } catch (error) {
}
catch (error) {
console.log(error); console.log(error);
res.status(500).json({error : "Internal server error"}); res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -98,6 +93,6 @@ async function deleteOrganizationPost(req, res){
// means making a JavaScript function defined in one // means making a JavaScript function defined in one
// module available for use in another module. // module available for use in another module.
module.exports = { module.exports = {
createOrganizationPost, createOrganizationPost,
deleteOrganizationPost deleteOrganizationPost
}; };

View File

@ -22,21 +22,19 @@ const organization_model = require('../models/organization_model');
* *
* @returns the inserted organization * @returns the inserted organization
*/ */
async function createOrganization(req, res){ async function createOrganization (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.name) { if (!req.body.name) {
return res.status(400).json({ error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' });
} }
try{ try {
const organization = organization_model.organization(req.body.name, req.body.location, req.body.description, req.body.is_hiring); const organization = organization_model.organization(req.body.name, req.body.location, req.body.description, req.body.is_hiring);
await organization_model.insertOrganization(organization, req.jwt.person_id); await organization_model.insertOrganization(organization, req.jwt.person_id);
return res.status(200).json({ Organization: organization }); return res.status(200).json({ Organization: organization });
} } catch (error) {
catch (error){
console.error('Error creating Organization:', error); console.error('Error creating Organization:', error);
res.status(500).json({error : "Internal server error"}); res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -46,42 +44,39 @@ async function createOrganization(req, res){
* *
* Required field(s): none. * Required field(s): none.
*/ */
async function updateOrganization(req, res){ async function updateOrganization (req, res) {
const updateOrganization = {}; const updateOrganization = {};
if(req.body.name){ if (req.body.name) {
updateOrganization.name = req.body.name; updateOrganization.name = req.body.name;
} }
if(req.body.location){ if (req.body.location) {
updateOrganization.location = req.body.location; updateOrganization.location = req.body.location;
} }
if(req.body.description){ if (req.body.description) {
updateOrganization.description = req.body.description; updateOrganization.description = req.body.description;
} }
if(req.body.is_hiring){ if (req.body.is_hiring) {
updateOrganization.is_hiring = req.body.is_hiring; updateOrganization.is_hiring = req.body.is_hiring;
} }
if (Object.keys(updateOrganization).length === 0) { if (Object.keys(updateOrganization).length === 0) {
return res.status(400).json({ error : "Bad request. No data to update"}); return res.status(400).json({ error: 'Bad request. No data to update' });
} }
try { try {
const isUpdateSuccessful = organization_model.updateOrganizationIfAdministrator(updateOrganization, req.params.id, req.jwt.person_id); const isUpdateSuccessful = organization_model.updateOrganizationIfAdministrator(updateOrganization, req.params.id, req.jwt.person_id);
if(isUpdateSuccessful){ if (isUpdateSuccessful) {
return res.status(200).json({ success : "true"}); return res.status(200).json({ success: 'true' });
} else {
return res.status(404).json({ error: 'Organization either not found or insufficient permissions' });
} }
else{ } catch (error) {
return res.status(404).json({error : "Organization either not found or insufficient permissions"});
}
}
catch (error) {
console.log(error); console.log(error);
return res.status(500).json({error : "Internal server error"}); return res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -91,19 +86,17 @@ async function updateOrganization(req, res){
* Deletes the specified organization if the logged user is * Deletes the specified organization if the logged user is
* one of its administrator * one of its administrator
*/ */
async function deleteOrganization(req, res){ async function deleteOrganization (req, res) {
try { try {
const isDeleteSuccessful = organization_model.deleteOrganizationIfAdmin(req.params.id, req.jwt.person_id); const isDeleteSuccessful = organization_model.deleteOrganizationIfAdmin(req.params.id, req.jwt.person_id);
if(isDeleteSuccessful){ if (isDeleteSuccessful) {
return res.status(403).json({error: "Forbidden"}); return res.status(403).json({ error: 'Forbidden' });
} else {
return res.status(200).json({ success: true });
} }
else{ } catch (error) {
return res.status(200).json({success: true});
}
}
catch (error) {
console.error(error); console.error(error);
return res.status(500).json({error : "Internal server error"}); return res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -116,19 +109,17 @@ async function deleteOrganization(req, res){
* *
* @returns the organization. * @returns the organization.
*/ */
async function getOrganization(req, res){ async function getOrganization (req, res) {
try { try {
const organization = await organization_model.getOrganizationById(req.params.id); const organization = await organization_model.getOrganizationById(req.params.id);
if(organization) { if (organization) {
return res.status(200).json(organization); return res.status(200).json(organization);
} else {
return res.status(404).json({ error: 'Not found' });
} }
else{ } catch (error) {
return res.status(404).json({error : "Not found"}); console.error('Error retrieving an organization: ' + error);
} return res.status(500).json({ error: 'Internal server error' });
}
catch (error) {
console.error("Error retrieving an organization: " + error);
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -138,4 +129,3 @@ module.exports = {
updateOrganization, updateOrganization,
deleteOrganization deleteOrganization
}; };

View File

@ -27,18 +27,17 @@ const person_model = require('../models/person_model');
* *
* @returns The activationlink identifier * @returns The activationlink identifier
*/ */
async function registerPerson(req, res){ async function registerPerson (req, res) {
// Does this server allow users to register? // Does this server allow users to register?
if (process.env.ALLOW_USER_REGISTRATION === 'false'){ if (process.env.ALLOW_USER_REGISTRATION === 'false') {
return res.status(403).json({error : "Users cannot register on this server"}); return res.status(403).json({ error: 'Users cannot register on this server' });
} }
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.display_name || !req.body.email || !req.body.password) { if (!req.body.display_name || !req.body.email || !req.body.password) {
return res.status(400).json({ error : "Some or all required fields are missing"}); return res.status(400).json({ error: 'Some or all required fields are missing' });
} }
if(!validator.validateEmail(req.body.email)){ if (!validator.validateEmail(req.body.email)) {
return res.status(400).json({ error : "The email is not in a valid format"}); return res.status(400).json({ error: 'The email is not in a valid format' });
} }
// Generate activation link token // Generate activation link token
@ -46,11 +45,11 @@ async function registerPerson(req, res){
// Hash provided password // Hash provided password
const hashPasswordPromise = bcrypt.hash(req.body.password, 10); const hashPasswordPromise = bcrypt.hash(req.body.password, 10);
try{ try {
// Check whether e-mail exists already (enforced by database constraints) // Check whether e-mail exists already (enforced by database constraints)
const existingUser = await person_model.getPersonByEmail(req.body.email); const existingUser = await person_model.getPersonByEmail(req.body.email);
if(existingUser){ if (existingUser) {
return res.status(409).json({ error: "E-mail already in use" }); return res.status(409).json({ error: 'E-mail already in use' });
} }
const personToInsert = person_model.person( const personToInsert = person_model.person(
req.body.email, req.body.email,
@ -61,11 +60,10 @@ async function registerPerson(req, res){
true, true,
req.body.place_of_living); req.body.place_of_living);
await person_model.registerPerson(personToInsert, activationLink); await person_model.registerPerson(personToInsert, activationLink);
return res.status(200).json({ activationLink: activationLink }); return res.status(200).json({ activationLink });
} } catch (error) {
catch (error){
console.error('Error registering person:', error); console.error('Error registering person:', error);
res.status(500).json({error : "Internal server error"}); res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -79,25 +77,23 @@ async function registerPerson(req, res){
* *
* @returns The token * @returns The token
*/ */
async function login(req, res){ async function login (req, res) {
// Ensure that the required fields are present before proceeding // Ensure that the required fields are present before proceeding
if (!req.body.email || !req.body.password) { if (!req.body.email || !req.body.password) {
return res.status(400).json({error : "Invalid request"}); return res.status(400).json({ error: 'Invalid request' });
} }
try{ try {
const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password); const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password);
if (person){ if (person) {
const token = jwt_utils.generateToken(person.id); const token = jwt_utils.generateToken(person.id);
res.status(200).json({token: token }); res.status(200).json({ token });
} else {
res.status(401).json({ error: 'Unauthorized' });
} }
else{ } catch (error) {
res.status(401).json({error : "Unauthorized"});
}
} catch(error){
console.error('Error logging in: ', error); console.error('Error logging in: ', error);
res.status(500).json({error : "Internal server error"}); res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -110,21 +106,20 @@ async function login(req, res){
* *
* @returns The Person * @returns The Person
*/ */
async function getPerson(req, res){ async function getPerson (req, res) {
try { try {
const person = await person_model.getPersonById(req.params.id); const person = await person_model.getPersonById(req.params.id);
if(person){ if (person) {
// I am retrieving either myself or an enabled user // I am retrieving either myself or an enabled user
if(person.id == req.jwt.person_id || person.enabled){ if (person.id == req.jwt.person_id || person.enabled) {
delete person['password']; // remove password field for security reasons delete person.password; // remove password field for security reasons
return res.status(200).send(person); return res.status(200).send(person);
} }
} }
return res.status(404).json({error: "Not found"}); return res.status(404).json({ error: 'Not found' });
} } catch (error) {
catch (error) { console.log('Error while getting person: ' + error);
console.log("Error while getting person: " + error); return res.status(500).json({ error: 'Internal server error' });
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -136,18 +131,17 @@ async function getPerson(req, res){
* *
* @returns Person's details * @returns Person's details
*/ */
async function getMyself(req, res){ async function getMyself (req, res) {
try{ try {
const person = await person_model.getPersonById(req.jwt.person_id); const person = await person_model.getPersonById(req.jwt.person_id);
if(person){ if (person) {
delete person['password']; delete person.password;
return res.status(200).send(person); return res.status(200).send(person);
} }
return res.status(404).json({error: "Not found"}); return res.status(404).json({ error: 'Not found' });
} } catch (error) {
catch (error){ console.log('Error while getting myself: ' + error);
console.log("Error while getting myself: " + error); return res.status(500).json({ error: 'Internal server error' });
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -161,61 +155,57 @@ async function getMyself(req, res){
* new_password if updating the password. * new_password if updating the password.
* *
*/ */
async function updatePerson(req, res){ async function updatePerson (req, res) {
if (req.jwt.person_id != req.params.id) {
if (req.jwt.person_id != req.params.id){ return res.status(403).json({ error: 'Forbidden' });
return res.status(403).json({ error : "Forbidden"});
} }
const updatePerson = {}; const updatePerson = {};
if(req.body.display_name){ if (req.body.display_name) {
updatePerson.display_name = req.body.display_name; updatePerson.display_name = req.body.display_name;
} }
if(req.body.date_of_birth){ if (req.body.date_of_birth) {
if(validator.isPostgresDateFormatValid(req.body.date_of_birth)){ if (validator.isPostgresDateFormatValid(req.body.date_of_birth)) {
updatePerson.date_of_birth = req.body.date_of_birth; updatePerson.date_of_birth = req.body.date_of_birth;
} } else {
else{ return res.status(400).json({ error: 'Date of birth format not valid. Please specify a YYYY-MM-DD date' });
return res.status(400).json({ error : "Date of birth format not valid. Please specify a YYYY-MM-DD date"});
} }
} }
if(req.body.available){ if (req.body.available) {
updatePerson.available = req.body.available; updatePerson.available = req.body.available;
} }
if(req.body.place_of_living){ if (req.body.place_of_living) {
updatePerson.place_of_living = req.body.place_of_living; updatePerson.place_of_living = req.body.place_of_living;
} }
// If we are tying to change password, the old password must be provided // If we are tying to change password, the old password must be provided
if(req.body.old_password && req.body.new_password){ if (req.body.old_password && req.body.new_password) {
const user = await knex('Person') const user = await knex('Person')
.select('password') .select('password')
.where({ id: req.jwt.person_id }) .where({ id: req.jwt.person_id })
.first(); .first();
const passwordMatches = await bcrypt.compare(req.body.old_password, user.password); const passwordMatches = await bcrypt.compare(req.body.old_password, user.password);
if(passwordMatches){ if (passwordMatches) {
updatePerson.password = await bcrypt.hash(req.body.new_password, 10); updatePerson.password = await bcrypt.hash(req.body.new_password, 10);
} } else {
else{ return res.status(401).json({ error: 'Password verification failed' });
return res.status(401).json({ error : "Password verification failed"}); }
}
} }
if (Object.keys(updatePerson).length === 0) { if (Object.keys(updatePerson).length === 0) {
return res.status(400).json({ error : "Bad request. No data to update"}); return res.status(400).json({ error: 'Bad request. No data to update' });
} }
try { try {
await person_model.updatePerson(updatePerson, req.params.id); await person_model.updatePerson(updatePerson, req.params.id);
return res.status(200).json({ success : "true"}); return res.status(200).json({ success: 'true' });
} } catch (error) {
catch (error) { console.log('Error while updating a Person: ' + error);
console.log("Error while updating a Person: " + error); return res.status(500).json({ error: 'Internal server error' });
return res.status(500).json({ error : "Internal server error"});
} }
} }
@ -228,15 +218,14 @@ async function updatePerson(req, res){
* Required field(s): none * Required field(s): none
* *
*/ */
async function deletePerson(req, res) { async function deletePerson (req, res) {
// TODO: Delete Organization if this user was its only administrator // TODO: Delete Organization if this user was its only administrator
try { try {
await person_model.deletePerson(req.jwt.person_id); await person_model.deletePerson(req.jwt.person_id);
return res.status(200).json({success: true}); return res.status(200).json({ success: true });
} } catch (error) {
catch (error) { console.log('Error deleting a Person: ' + error);
console.log("Error deleting a Person: " + error); return res.status(500).json({ error: 'Internal server error' });
return res.status(500).json({error : "Internal server error"});
} }
} }
@ -244,10 +233,10 @@ async function deletePerson(req, res) {
// means making a JavaScript function defined in one // means making a JavaScript function defined in one
// module available for use in another module. // module available for use in another module.
module.exports = { module.exports = {
registerPerson, registerPerson,
login, login,
getPerson, getPerson,
getMyself, getMyself,
updatePerson, updatePerson,
deletePerson deletePerson
}; };

View File

@ -13,39 +13,39 @@
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
function generateToken(person_id) { function generateToken (person_id) {
// The payload the JWT will carry within itself // The payload the JWT will carry within itself
const payload = { const payload = {
person_id: person_id person_id
}; };
const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, { const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, {
expiresIn: '8h' expiresIn: '8h'
}); });
return token; return token;
}
// Middlware
function verifyToken (req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).send({ error: 'No token provided' });
} }
// Middlware jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
function verifyToken(req, res, next) { if (err) {
const token = req.headers.authorization; return res.status(401).send({ error: 'Failed to authenticate token' });
if (!token) {
return res.status(401).send({error : 'No token provided'});
} }
jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => { // If the token is valid, store the decoded data in the request object
if (err) { // req.jwt will contain the payload created in generateToken
return res.status(401).send({error : 'Failed to authenticate token'}); req.jwt = decoded;
} next();
});
}
// If the token is valid, store the decoded data in the request object module.exports = {
// req.jwt will contain the payload created in generateToken generateToken,
req.jwt = decoded; verifyToken
next();
});
}
module.exports = {
generateToken,
verifyToken
}; };

View File

@ -12,14 +12,14 @@
*/ */
const knexInstance = require('knex')({ const knexInstance = require('knex')({
client: 'pg', client: 'pg',
connection: { connection: {
host: process.env.POSTGRES_SERVER, host: process.env.POSTGRES_SERVER,
user: process.env.POSTGRES_USERNAME, user: process.env.POSTGRES_USERNAME,
password: process.env.POSTGRES_PASSWORD, password: process.env.POSTGRES_PASSWORD,
port: process.env.POSTGRES_PORT, port: process.env.POSTGRES_PORT,
database: 'Blink' database: 'Blink'
} }
}); });
module.exports = knexInstance; module.exports = knexInstance;

View File

@ -16,9 +16,9 @@
* @param {*} email email to validate * @param {*} email email to validate
* @returns true or false * @returns true or false
*/ */
function validateEmail(email) { function validateEmail (email) {
const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/; const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
return regex.test(email); return regex.test(email);
} }
/** /**
@ -27,12 +27,12 @@ function validateEmail(email) {
* @param {*} dateString the date to validate * @param {*} dateString the date to validate
* @returns true or false * @returns true or false
*/ */
function isPostgresDateFormatValid(dateString) { function isPostgresDateFormatValid (dateString) {
const regex = /^\d{4}-\d{2}-\d{2}$/; const regex = /^\d{4}-\d{2}-\d{2}$/;
return regex.test(dateString); return regex.test(dateString);
} }
module.exports = { module.exports = {
validateEmail, validateEmail,
isPostgresDateFormatValid isPostgresDateFormatValid
}; };

View File

@ -7,24 +7,24 @@ require('dotenv').config({ path: '../src/.env' });
describe('Person Tests', () => { describe('Person Tests', () => {
test('Correct registration', async () => { test('Correct registration', async () => {
const response = await request(app) const response = await request(app)
.post('/api/register') .post('/api/register')
.send({ .send({
email : "johntestdoe@mail.org", email: 'johntestdoe@mail.org',
password : "password", password: 'password',
display_name : "John Doe" display_name: 'John Doe'
}) });
expect(response.status).toBe(200); expect(response.status).toBe(200);
expect(response.body).toEqual({ activationLink: expect.any(String) }); expect(response.body).toEqual({ activationLink: expect.any(String) });
}); });
test('Incorrect registration', async () => { test('Incorrect registration', async () => {
const response = await request(app) const response = await request(app)
.post('/api/register') .post('/api/register')
.send({ .send({
email : "this is not an email", email: 'this is not an email',
password : "password", password: 'password',
display_name : "John Doe" display_name: 'John Doe'
}) });
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
}); });

View File

@ -1 +1 @@
const apiUrl = "http://localhost:3000/blinkapi"; const apiUrl = 'http://localhost:3000/blinkapi';

View File

@ -1,31 +1,28 @@
// https://javascript.info/callbacks // https://javascript.info/callbacks
function execute_action(param, callback){ function execute_action (param, callback) {
if (param == 'something') {
if(param == "something"){ console.log('Executing action: ' + param);
console.log("Executing action: " + param); callback(null, Date.now());
callback(null, Date.now()); } else {
} // We can call callback with one argument even if
else{ // the signature states two parameters
// We can call callback with one argument even if callback(new Error('Invalid parameter'));
// the signature states two parameters }
callback(new Error("Invalid parameter"))
}
} }
function entryPoint(){ function entryPoint () {
/* ===== Begin Simple callback ===== */ /* ===== Begin Simple callback ===== */
execute_action("something", function (error, time_of_completion){ execute_action('something', function (error, time_of_completion) {
if(error){ if (error) {
console.log("Something happened"); console.log('Something happened');
} } else {
else{ console.log('Time of completion: ' + new Date(time_of_completion).toDateString());
console.log("Time of completion: " + new Date(time_of_completion).toDateString()); }
} });
}); console.log('I started here!');
console.log("I started here!"); /*
/*
Ciò è utile se ad esempio execute_action fa operazioni lente (ad esempio Ciò è utile se ad esempio execute_action fa operazioni lente (ad esempio
scrittura su database, connessioni HTTP ecc..) ma abbiamo bisogno scrittura su database, connessioni HTTP ecc..) ma abbiamo bisogno
del suo valore di ritorno per continuare una determinata operazione del suo valore di ritorno per continuare una determinata operazione
@ -38,7 +35,7 @@ function entryPoint(){
comprendere le basi di questo meccanismo. comprendere le basi di questo meccanismo.
*/ */
/* ===== End Simple Callback ===== */ /* ===== End Simple Callback ===== */
} }
entryPoint(); entryPoint();

View File

@ -10,24 +10,24 @@
Remember that Promises are not intrensically asyncronous Remember that Promises are not intrensically asyncronous
*/ */
let promise = new Promise(function(resolve, reject) { const promise = new Promise(function (resolve, reject) {
setTimeout(() => resolve("done"), 500); setTimeout(() => resolve('done'), 500);
}); });
/* /*
The first argument of .then is a function that runs when the promise is resolved and receives the result. The first argument of .then is a function that runs when the promise is resolved and receives the result.
The second argument of .then is a function that runs when the promise is rejected and receives the error. The second argument of .then is a function that runs when the promise is rejected and receives the error.
*/ */
promise.then( promise.then(
result => console.log("The operation was successful. It returned " + result), result => console.log('The operation was successful. It returned ' + result),
error => console.log("The operation was not successful: " + error) error => console.log('The operation was not successful: ' + error)
); );
/* /*
Or we can pass only one argument if we're interested only in a positive result Or we can pass only one argument if we're interested only in a positive result
*/ */
promise.then( promise.then(
result => console.log("The operation was successful. It returned " + result) result => console.log('The operation was successful. It returned ' + result)
); );
/* /*
@ -37,12 +37,12 @@ promise.then(
promise.catch internally just calls promise.then(null, f) promise.catch internally just calls promise.then(null, f)
*/ */
promise.catch( promise.catch(
error => console.log(error) error => console.log(error)
); );
/* /*
finally gets always called finally gets always called
*/ */
promise.finally( promise.finally(
() => console.log("The execution has terminated. Bye") () => console.log('The execution has terminated. Bye')
); );

View File

@ -3,26 +3,18 @@
// .then() returns a new Promise when you do "return", // .then() returns a new Promise when you do "return",
// internally calling resolve(). // internally calling resolve().
new Promise(function(resolve, reject) { new Promise(function (resolve, reject) {
setTimeout(() => resolve(1), 1);
setTimeout(() => resolve(1), 1); }).then(function (result) {
console.log(result);
}).then(function(result) { return result * 2;
}).then(function (result) {
console.log(result); console.log(result);
return result * 2; return result * 2;
}).then(function (result) {
}).then(function(result) { console.log(result);
return result * 2;
console.log(result); });
return result * 2;
}).then(function(result) {
console.log(result);
return result * 2;
});
/* /*
It will print It will print
@ -45,12 +37,12 @@ new Promise(function(resolve, reject) {
fetch('http://www.fsf.org') fetch('http://www.fsf.org')
// .then below runs when the remote server responds // .then below runs when the remote server responds
.then(function(response) { .then(function (response) {
// response.text() returns a new promise that resolves with the full response text // response.text() returns a new promise that resolves with the full response text
// when it loads // when it loads
return response.text(); return response.text();
}) })
.then(function(text) { .then(function (text) {
// ...and here's the content of the remote file // ...and here's the content of the remote file
console.log(text); console.log(text);
}); });

View File

@ -3,63 +3,62 @@
// A async function always returns a promise. Other values are wrapped in a resolved promise automatically. // A async function always returns a promise. Other values are wrapped in a resolved promise automatically.
// Async e Await sono solo "sintassi zuccherina" per rendere l'utilizzo delle Promise più semplice // Async e Await sono solo "sintassi zuccherina" per rendere l'utilizzo delle Promise più semplice
async function f1() { async function f1 () {
return 1; return 1;
} }
f1().then(console.log); // 1 f1().then(console.log); // 1
// The keyword await makes JavaScript wait until that promise settles and returns its result. // The keyword await makes JavaScript wait until that promise settles and returns its result.
// It can be used in async functions only // It can be used in async functions only
// Lets emphasize: await literally suspends the function execution until the promise settles, // Lets emphasize: await literally suspends the function execution until the promise settles,
// and then resumes it with the promise result. // and then resumes it with the promise result.
async function f2() { async function f2 () {
const promise = new Promise((resolve, reject) => {
let promise = new Promise((resolve, reject) => { setTimeout(() => resolve('done!'), 1000);
setTimeout(() => resolve("done!"), 1000)
}); });
let result = await promise; // wait until the promise resolves (*) const result = await promise; // wait until the promise resolves (*)
console.log(result); // "done!" console.log(result); // "done!"
} }
f2(); f2();
// Tutto il codice nella stessa funzione (o nello stesso blocco di codice) // Tutto il codice nella stessa funzione (o nello stesso blocco di codice)
// dopo "await" è da considerarsi nel "then()" di una promessa. Pertanto dopo // dopo "await" è da considerarsi nel "then()" di una promessa. Pertanto dopo
// await (ma prima del completamento della Promise), // await (ma prima del completamento della Promise),
// il flusso di esecuzione va fuori a quel blocco di codice. Ad esempio considera // il flusso di esecuzione va fuori a quel blocco di codice. Ad esempio considera
// il seguente esempio: // il seguente esempio:
async function exampleAsyncFunction() { async function exampleAsyncFunction () {
console.log('Before await'); console.log('Before await');
await new Promise(function(resolve, reject) { await new Promise(function (resolve, reject) {
setTimeout(() => resolve("done"), 500); setTimeout(() => resolve('done'), 500);
}); // Pauses execution here until the promise resolves. }); // Pauses execution here until the promise resolves.
console.log('After await'); console.log('After await');
} }
console.log('Start'); console.log('Start');
exampleAsyncFunction(); exampleAsyncFunction();
console.log('End'); console.log('End');
// Il risultato sarà: // Il risultato sarà:
// Start, Before Await, End, After await // Start, Before Await, End, After await
// Viene prima "End" e poi "After Await", perché // Viene prima "End" e poi "After Await", perché
// dopo await, il flusso di esecuzione ritorna al // dopo await, il flusso di esecuzione ritorna al
// chiamante // chiamante
// Domande // Domande
// //
// Why await only works in async function in javascript? // Why await only works in async function in javascript?
// https://stackoverflow.com/questions/49640647/why-await-only-works-in-async-function-in-javascript // https://stackoverflow.com/questions/49640647/why-await-only-works-in-async-function-in-javascript
// //
// Why using async-await // Why using async-await
// https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await // https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await
// //
// Si faccia presente che non è possibile creare da zero una funzione asincrona (come // Si faccia presente che non è possibile creare da zero una funzione asincrona (come
// setInterval o fetch) // setInterval o fetch)
// https://stackoverflow.com/questions/61857274/how-to-create-custom-asynchronous-function-in-javascript // https://stackoverflow.com/questions/61857274/how-to-create-custom-asynchronous-function-in-javascript
// //
// Altra domanda interessante // Altra domanda interessante
// https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await // https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await

View File

@ -3,10 +3,10 @@
The function delay(ms) should return a promise. That promise should resolve after ms milliseconds, so that we can add .then to it, like this: The function delay(ms) should return a promise. That promise should resolve after ms milliseconds, so that we can add .then to it, like this:
*/ */
function delay(ms){ function delay (ms) {
return new Promise(resolve => { return new Promise(resolve => {
setTimeout(resolve, ms); setTimeout(resolve, ms);
}); });
} }
delay(1000).then(() => console.log("Hello world!")); delay(1000).then(() => console.log('Hello world!'));