[KnackSteem API] - New Functions and some fix
Repository & Pull Requests
https://github.com/knacksteem/knacksteem-api/
https://github.com/knacksteem/knacksteem-api/pull/14
https://github.com/knacksteem/knacksteem-api/pull/15
What is KnackSteem?
"Do you have any talent? If yes! then KnackSteem is for you."
"Rewards people with talents, it can be any talent, anything you know how to do best is highly welcome on the platform. "
Source: Discord Channel :D
Changes Made
Endpoint to edit post tags (on backend only)
We use tags to improve our posts search functionality. This point will allow the post owner to edit the tags of this post if a mistake was made. This endpoint will require prior authentification before using it.
Related code:
/**
* Method to update the tags of a post
* @param {Object} req: url params
* @param {Function} res: Express.js response callback
* @param {Function} next: Express.js middleware callback
* @author Jayser Mendez
* @public
*/
exports.updateTags = async (req, res, next) => {
try {
const { permlink, tags } = req.body;
const { username } = res.locals;
const post = await Post.findOne({ permlink });
if (post.author !== username) {
return next({
status: httpStatus.UNAUTHORIZED,
message: 'You cannot edit someone else post',
});
}
await Post.update({ permlink }, { tags });
return res.status(httpStatus.OK).send({
status: httpStatus.OK,
message: 'Post edited correctly',
});
} catch (err) {
console.log(err);
return next({
status: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Opps! Something is wrong in our server. Please report it to the administrator.',
error: err,
});
}
};
Refactor fields in stats endpoints
By default, stats endpoints were not showing if the current user has voted the current loaded post. Indeed, there was an inconsistency in the returned fields in comparison with other endpoints. Also, this was leading to errors in client-side since it is re-using components for the posts. To solve this issue, I replaced the username field (used to get posts by the user) to author and used the username field to confirm is the current user has voted this post. The isVoted field was added to the response to make it identically to the rest.
Related code (coded was truncated to improve visibility):
let isVoted = false;
// Check if there is a user provided in the params.
// If so, determine if this user has voted the post.
if (username) {
isVoted = helper.isVoted(response.active_votes, username);
}
Allow all CORS method in Development mode
Even I was not facing this issue, the frontend developer told me that CORS were denying the PUT method at his end. To solve this issue, we decided to allow all methods in development mode (and we've confirmed that it works properly in production mode).
Related code:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
next();
});
Categories model
Categories were loaded directly from the client side. As an enhancement, I decided to load them dynamically from the server side since we may need to add or remove some categories in the future. to avoid a re-deploy of the client-side, those should be loaded from the backend.
Related code:
const mongoose = require('mongoose');
/**
* Category Schema
* @author Jayser Mendez
* @private
*/
const categorySchema = new mongoose.Schema({
key: {
type: String,
required: true,
trim: true,
lowercase: true,
unique: true,
},
name: {
type: String,
required: true,
trim: true,
},
});
// Declare index in category schema for faster query.
categorySchema.index({
key: 1,
name: 1,
}, { name: 'category_index' });
/**
* @typedef Category
*/
module.exports = mongoose.model('Category', categorySchema);
Endpoint to get all categories
As explained above, categories are now dynamically loaded. Hence, an endpoint is needed to consume them. The endpoint will return a response in the exact way it was in the client side so not too much changes are needed.
Related code:
/**
* Get all the categories in database
* @param {Object} req: url params
* @param {Function} res: Express.js response callback
* @param {Function} next: Express.js middleware callback
* @public
* @author Jayser Mendez
*/
exports.getCategories = async (req, res, next) => {
try {
const categoriesList = await Category.find({}, { __v: 0, _id: 0 });
return res.status(httpStatus.OK).send({
status: httpStatus.OK,
results: categoriesList,
count: categoriesList.length,
});
} catch (err) {
return next({
status: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Opps! Something is wrong in our server. Please report it to the administrator.',
error: err,
});
}
};
Endpoint to add category
Since now categories are dynamic, a supervisor may be able to create categories. So, this endpoint was created with this functionality in mind.
Related code:
/**
* Create a new category in database
* @param {Object} req: url params
* @param {Function} res: Express.js response callback
* @param {Function} next: Express.js middleware callback
* @public
* @author Jayser Mendez
*/
exports.createCategory = async (req, res, next) => {
try {
const { name } = req.body;
// Strip all the empty spaces and special characters and convert to lower case
const key = name.replace(/[^A-Z0-9]+/ig, '').toLowerCase();
const newCategory = await new Category({ key, name });
await Category.create(newCategory);
return res.status(httpStatus.CREATED).send({
status: httpStatus.CREATED,
message: 'Category was correctly created',
});
} catch (err) {
// Return exact error if there is a collision in the key
if (err.message.includes('E11000')) {
return next({
status: httpStatus.NOT_ACCEPTABLE,
message: 'Opps! A category with this name already exists.',
error: err,
});
}
return next({
status: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Opps! Something is wrong in our server. Please report it to the administrator.',
error: err,
});
}
};
Seed method to insert default categories
A set of default categories should be added when the server run for the first time. To do so, I created a seed method to insert a batch of data in the database if the categories collection is empty.
Related code:
const Category = require('../models/category.model');
// Batch of initial categories
const initialCategories = [
{ key: 'vlog', name: 'VLog' },
{ key: 'graphics', name: 'Graphics' },
{ key: 'art', name: 'Art' },
{ key: 'knack', name: 'Knack' },
{ key: 'onealtruism', name: 'One Altruism' },
{ key: 'music', name: 'Music' },
{ key: 'humor', name: 'Joke/Humor' },
{ key: 'inspiring', name: 'Inspiring' },
{ key: 'visibility', name: 'Visibility' },
{ key: 'news', name: 'News' },
{ key: 'quotes', name: 'Quotes' },
{ key: 'techtrends', name: 'Tech Trends' },
{ key: 'blogposts', name: 'Blog Posts' },
];
/**
* Method to insert the initial batch of categories in the backend
* @public
* @author Jayser Mendez
*/
exports.seedCategories = async () => {
try {
const count = await Category.count();
// If this is the first time running, insert the categories
if (count === 0) {
initialCategories.map(async (category) => {
const newCategory = await new Category({
key: category.key,
name: category.name,
});
await Category.create(newCategory);
});
return true;
}
return false;
} catch (err) {
return false;
}
};
Contributors
Feel free to contact @knowledges :)
Hey! I'm pleased to see this project grow.
Let's go for the review:
You should separate the fixes and features, not put them in the same PR.
https://github.com/knacksteem/knacksteem-api/pull/14/files#diff-2dbc47226b708066000a2b1df9653f12R69 => I find this weird, the status is sent in the response header and then as JSON. Any reason for duplicating this information ?
You have in your comments
@param {Object} req: url params
=> This is not accurate. The request contains much more information than just the url params.+1 for using express validate.
https://github.com/knacksteem/knacksteem-api/pull/15/files#diff-2dbc47226b708066000a2b1df9653f12R61 => You could do one query by using
findOneAndUpdate
with permlink & author as search params.Overall this is great work! Congratulations :)
See you for another contribution
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Hey @gregory.latinier
Here's a tip for your valuable feedback! @Utopian-io loves and incentivises informative comments.
Contributing on Utopian
Learn how to contribute on our website.
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
Hey @jaysermendez
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
Congratulations @jaysermendez! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes received
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP
To support your work, I also upvoted your post!
Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - The results, the winners and the prizes
Congratulations @jaysermendez! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP
Congratulations @jaysermendez! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
Award for the number of upvotes
Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard:
Congratulations @jaysermendez! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard:
Congratulations @jaysermendez! You received a personal award!
Click here to view your Board
Do not miss the last post from @steemitboard:
Dear @jaysermendez
I just decided to visit your profile and check out your latest content only to notice that you didn't blog in quite a while already.
Hope you didn't give up completely on Steemit?
Cheers, Piotr