Following API best practices

This commit is contained in:
xfarrow 2024-03-06 10:19:37 +01:00
parent 4f53ef7561
commit ae14f04949
10 changed files with 46 additions and 38 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,5 @@
# APIs
## Programming language
The Blink APIs are currently written in NodeJS only.
## Prerequisites
* PostgreSQL;
* NodeJS.
@ -16,3 +13,8 @@ In order to deploy the Blink APIs, follow these steps:
You can test the APIs in two ways:
* Open `BlinkApiUsageExample.json` with Insomnia or Bruno in order to have the collection of APIs already configured and ready to be seen in action;
* Run `npm test` in `./nodejs` to run a suite of automated tests.
## For Developers
The current implementation of the Blink APIs is written in NodeJS. Feel free to develop them in any other
programming language (you can paste the folder here) but make sure to make them compatible with one another
and please follow the API design [best practices](https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design)

View File

@ -38,8 +38,9 @@ async function isPersonOrganizationAdministrator(personId, organizationId) {
*/
async function addOrganizationAdministrator(personId, organizationId, requester) {
const isPersonAdmin = await organization_admin_model.isPersonAdmin(requester, organizationId);
if (isPersonAdmin) {
const isRequesterAdmin = await isPersonOrganizationAdministrator(requester, organizationId);
const isPersonAdmin = await isPersonOrganizationAdministrator(personId, organizationId);
if (isRequesterAdmin && !isPersonAdmin) {
await knex('OrganizationAdministrator')
.insert({
id_person: personId,

View File

@ -25,14 +25,14 @@ const jwtUtils = require('../utils/middleware_utils');
*/
async function addOrganizationAdmin(req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.person_id) {
if (!req.params.id || !req.body.person_id) {
return res.status(400).json({
error: 'Invalid request'
});
}
try {
const success = await organizationAdminModel.addOrganizationAdministrator(req.body.person_id, req.body.organization_id, req.jwt.person_id);
const success = await organizationAdminModel.addOrganizationAdministrator(req.body.person_id, req.params.id, req.jwt.person_id);
if (success) {
return res.status(200).json({
success: true
@ -60,14 +60,14 @@ async function addOrganizationAdmin(req, res) {
*/
async function removeOrganizationAdmin(req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id) {
if (!req.params.organizationId) {
return res.status(400).json({
error: 'Invalid request'
});
}
try {
await organizationAdminModel.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id);
await organizationAdminModel.removeOrganizationAdmin(req.jwt.person_id, req.params.organizationId);
return res.status(200).json({
success: true
});
@ -81,8 +81,8 @@ async function removeOrganizationAdmin(req, res) {
const protectedRoutes = express.Router();
protectedRoutes.use(jwtUtils.verifyToken);
protectedRoutes.post('/organization/admin', addOrganizationAdmin);
protectedRoutes.delete('/organization/admin', removeOrganizationAdmin);
protectedRoutes.post('/organizations/:id/admins', addOrganizationAdmin);
protectedRoutes.delete('/organizations/:organizationId/admins/me', removeOrganizationAdmin);
module.exports = {
protectedRoutes

View File

@ -25,20 +25,25 @@ const jwtUtils = require('../utils/middleware_utils');
*/
async function createOrganizationPost(req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.content) {
if (!req.params.idOrganization || !req.body.content) {
return res.status(400).json({
error: 'Invalid request'
});
}
const organization = organizationPostModel.createOrganizationPost(
req.body.organization_id,
const organizationPost = organizationPostModel.createOrganizationPost(
req.params.idOrganization,
req.body.content,
req.jwt.person_id);
try {
const insertedOrganization = await organizationPostModel.insertOrganizationPost(organization);
return res.status(200).json(insertedOrganization);
const insertedOrganization = await organizationPostModel.insertOrganizationPost(organizationPost);
if(!!insertedOrganization){
return res.status(200).json(insertedOrganization);
}
return res.status(401).json({
error: 'Forbidden'
});
} catch (error) {
console.error(`Error in function ${createOrganizationPost.name}: ${error}`);
return res.status(500).json({
@ -78,8 +83,8 @@ async function deleteOrganizationPost(req, res) {
const protectedRoutes = express.Router();
protectedRoutes.use(jwtUtils.verifyToken);
protectedRoutes.post('/organization/post', createOrganizationPost);
protectedRoutes.delete('/organization/post/:id', deleteOrganizationPost);
protectedRoutes.post('/organizations/:idOrganization/posts', createOrganizationPost);
protectedRoutes.delete('/organizations/posts/:id', deleteOrganizationPost);
// Exporting a function
// means making a JavaScript function defined in one

View File

@ -147,13 +147,13 @@ async function getOrganization(req, res) {
}
const publicRoutes = express.Router();
publicRoutes.get('/organization/:id', getOrganization);
publicRoutes.get('/organizations/:id', getOrganization);
const protectedRoutes = express.Router();
protectedRoutes.use(jwtUtils.verifyToken);
protectedRoutes.post('/organization', createOrganization);
protectedRoutes.put('/organization/:id', updateOrganization);
protectedRoutes.delete('/organization/:id', deleteOrganization);
protectedRoutes.post('/organizations', createOrganization);
protectedRoutes.put('/organizations/:id', updateOrganization);
protectedRoutes.delete('/organizations/:id', deleteOrganization);
module.exports = {
publicRoutes,

View File

@ -92,7 +92,7 @@ async function registerPerson(req, res) {
*
* @returns The token
*/
async function login(req, res) {
async function createTokenByEmailAndPassword(req, res) {
// Ensure that the required fields are present before proceeding
if (!req.body.email || !req.body.password) {
return res.status(400).json({
@ -113,7 +113,7 @@ async function login(req, res) {
});
}
} catch (error) {
console.error(`Error in function ${login.name}: ${error}`);
console.error(`Error in function ${createTokenByEmailAndPassword.name}: ${error}`);
return res.status(500).json({
error: 'Internal server error'
});
@ -312,16 +312,16 @@ async function confirmActivation(req, res) {
}
const publicRoutes = express.Router(); // Routes not requiring token
publicRoutes.post('/register', registerPerson);
publicRoutes.post('/login', login);
publicRoutes.get('/person/:id/details', getPerson);
publicRoutes.get('/person/activation', confirmActivation);
publicRoutes.post('/persons', registerPerson);
publicRoutes.post('/persons/me/token', createTokenByEmailAndPassword);
publicRoutes.get('/persons/:id/details', getPerson);
publicRoutes.get('/persons/me/activation', confirmActivation);
const protectedRoutes = express.Router(); // Routes requiring token
protectedRoutes.use(jwtUtils.verifyToken);
protectedRoutes.get('/person/myself', getMyself);
protectedRoutes.put('/person', updatePerson);
protectedRoutes.delete('/person', deletePerson);
protectedRoutes.get('/persons/me', getMyself);
protectedRoutes.put('/persons/me', updatePerson);
protectedRoutes.delete('/persons/me', deletePerson);
// Exporting a function
// means making a JavaScript function defined in one

View File

@ -43,7 +43,7 @@
alert('Please fill in all fields');
return;
}
const response = await fetch(`${API_URL}/login`, {
const response = await fetch(`${API_URL}/persons/me/token`, {
method: "POST",
body: JSON.stringify({
email: email,
@ -59,7 +59,7 @@
if (response.ok) {
console.log(`Login was successful. Token is ${data.token}`);
document.cookie = `token=${data.token};`;
window.location.href = 'userprofile.html?id=myself';
window.location.href = 'userprofile.html?id=me';
} else {
alert(data.error);
}

View File

@ -65,7 +65,7 @@
}),
};
fetch(`${API_URL}/register`, options)
fetch(`${API_URL}/persons`, options)
.then(response => {
if (response.ok) {
alert("Congratulations! You've successfully registered to Blink." +

View File

@ -58,21 +58,21 @@
let response;
// Retrieving the logged in user's profile
if (!idToDisplay || idToDisplay === 'myself') {
if (!idToDisplay || idToDisplay === 'me') {
document.getElementById('editBadge').style.display = 'block'; // show edit button
const token = getCookie('token');
// Check whether the token exists
if (!token) {
window.location.href = 'login.html';
}
response = await fetch(`${API_URL}/person/myself`, {
response = await fetch(`${API_URL}/persons/me`, {
headers: {
"Content-type": "application/json; charset=UTF-8",
"authorization": token
}
});
} else {
response = await fetch(`${API_URL}/person/${idToDisplay}/details`, {
response = await fetch(`${API_URL}/persons/${idToDisplay}/details`, {
headers: {
"Content-type": "application/json; charset=UTF-8",
}