[KnackSteem] - API Features Added

in #utopian-io6 years ago

Repository & Pull Request

https://github.com/knacksteem/knacksteem-api
https://github.com/knacksteem/knacksteem-api/pull/3

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


Setup SteemConnect in backend

I wrote the code to set up a SteemConnect instance in the backend to validate incoming access token from the client side.

Related code:

const sc2 = require('sc2-sdk');
const config = require('./vars');

const api = sc2.Initialize({
  app: config.sc2_app, // SC2 App identifier here
  callbackURL: config.sc2_callback, // not really needed
});

module.exports = api;

Basically, it creates an instance of SteemConnect and makes it available to use within the project.


SteemConnect middleware

Since some actions require the user to be logged in but we didn't want to store their token, I decide to create this middleware to check if the provided access token is correct. It will basically initialize an instance of SteemConnect with the provided token and will try to query its profile. If there is a response, it will move to the next middleware. Otherwise, will throw an error.

Related code:

const sc2 = require('../../config/steemconnect');
const httpStatus = require('http-status');

/**
 * SC2 Validator. Validate if the provided access token is valid.
 * @author Jayser Mendez.
 * @public
 */
const validateSc2 = async (req, res, next) => {
  try {
    // Set the access token to the sc2 instance
    sc2.setAccessToken(req.body.access_token);

    // Call the sc2 api to validate the token.
    const sc2Res = await sc2.me();

    // Declare a local variable with the username.
    res.locals.username = sc2Res.user;

    // Declare a local variable with the user object.
    res.locals.userObject = sc2Res;

    // Move to the next middleware and pass username along.
    return next();

    // Catch the error if any.
  } catch (err) {
    // Send an error message in the response.
    return next({
      status: httpStatus.UNAUTHORIZED,
      message: 'Unauthorized access',
    });
  }
};

module.exports = validateSc2;

As explained before, it will validate the token and also, will make available two global variables including the username and the user object for the next middleware.


User exist middleware

Before introducing a new post in the database, it should check if the provided username exists (taking in mind that it already pass the SteemConnect middleware). If the user does not exist, the backend will create a user before proceeding to insert the post at the database.

Related code:

const User = require('../models/user.model');

/**
 * Username checker. Check if username exists in database, if not, create it.
 * @author Jayser Mendez.
 * @public
 */
const userExist = async (req, res, next) => {
  try {
    // Try to find the username in database.
    const user = await User.findOne({ username: res.locals.username });

    // If the username is the same as the one from locals, pass to next middleware.
    if (user) {
      return next();
    }

    // Otherwise, user does not exist, proceed to create it.
    // Create a new user object with the required data.
    const newUser = new User({
      username: res.locals.username,
      user: res.locals.userObject,
    });

    // Insert the new username in database.
    User.create(newUser);

    // Pass to the next middleware
    return next();


  // Catch any possible error.
  } catch (err) {
    // Catch errors here.
    return next;
  }
};

module.exports = userExist;

Created Post Schema (To be Edited)

I created the post schema (not a final version) in order to be able to test the endpoint. This schema has basic fields that are suitable for faster tests.

Code related:

const mongoose = require('mongoose');

/**
 * Post Schema
 * @private
 */
const postSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    trim: true,
  },
  description: {
    type: String,
    required: true,
  },
  tags: {
    type: Array,
    required: false,
  },
  author: {
    type: String,
    required: true,
    trim: true,
    lowercase: true,
  },
}, {
  timestamps: true,
});

// Declare index for createdAt property
postSchema.index({ createdAt: -1 });

/**
 * @typedef Post
 */
module.exports = mongoose.model('Post', postSchema);

Endpoints to get/create posts

First, I created a function to avoid rewriting the same code over and over. This function receives 4 parameters which can be used to determine what field will be used to make the query, how will it be sorted, how much posts will pull, and how many posts will it skips (useful for pagination).

Related code:

/**
 * Get posts from database based on criteria and sorting.
 * @param {Object} criteria: Fields to project from database
 * @param {Object} sort: Sorting strategy
 * @param {Number} limit: how much posts will be pulled
 * @param {Number} skip: how much posts will be skiped (useful for pagination)
 * @author Jayser Mendez
 * @private
 * @returns an array with the posts from database response
 */
const getPosts = async (criteria, sort, limit, skip) => {
  try {
    limit = parseInt(limit, 10);
    skip = parseInt(skip, 10);
    const postsList = await Post.find(criteria).sort(sort).limit(limit || 25).skip(skip || 0);
    return {
      results: postsList,
      count: postsList.length,
      status: 200,
    };
  } catch (err) {
    return err;
  }
};

Then, I created a function to pull all the posts in descending order from the database:

/**
 * Get all posts from database
 * @author Jayser Mendez
 * @public
 */
exports.getAllPosts = async (req, res, next) => {
  try {
    // Query the posts from database in a descending order.
    const { limit, skip } = req.query;
    const sort = { createdAt: -1 };
    const postsList = await getPosts({}, sort, limit, skip);

    // Send the posts to the client in a formatted JSON.
    return res.send(postsList);

  // Catch any possible error
  } catch (err) {
    return next(err);
  }
};

And finally, a function to get the posts from a specific user:

/**
 * Get all from a specific user from database
 * @author Jayser Mendez
 * @public
 */
exports.getPostsByAuthor = async (req, res, next) => {
  try {
    // Query the posts from database in a descending order.
    const { limit, skip } = req.query;
    const author = { author: req.query.username };
    const sort = { createdAt: -1 };
    const postsList = await getPosts(author, sort, limit, skip);

    // Send the posts to the client in a formatted JSON.
    return res.send(postsList);

  // Catch any possible error
  } catch (err) {
    return next(err);
  }
};

And to create a post, I made a function that will use the parameters from the middleware (if the access token is valid of course). When this function is reached, it will insert the post in the database with the provided fields.

/**
 * Insert a new post into database
 * @author Jayser Mendez
 * @public
 */
exports.createPost = async (req, res, next) => {
  try {
    // Initialize a new object with post data
    const newPost = new Post({
      title: req.body.title,
      description: req.body.description,
      author: res.locals.username,
      tags: req.body.tags || [],
    });

    // Insert the post into database.
    Post.create(newPost);

    res.send({
      status: 200,
      message: 'Post created correctly',
    });

    // Move to the next middleware
    return next();

  // If any error, catch it
  } catch (error) {
    return next(error);
  }
};

What is next?


Next, I will create endpoints for the post master-details data and will create functions to parse post data from blockchain to propagate the data before sending it to the client side.

Sort:  

Hi, @jaysermendez.

Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork.

Knacksteem rewards individuals with unique talents and skills.

Join the offline community on Discord

Thanks for the contribution, @jaysermendez! I am glad @knowledges found someone like you to work on his project!

What is this? ;)

return res.json({ token: 'adadaada' });

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]

Thank you :D and this line is a dummy line hehehehe. It will be removed since it is not used hahahahah.

Hi, @jaysermendez. Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork. Knacksteem rewards individuals with unique talents and skills. Join the offline community on Discord

Hi, @jaysermendez. Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork. Knacksteem rewards individuals with unique talents and skills. Join the offline community on Discord

Hi, @jaysermendez. Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork.
Knacksteem rewards individuals with unique talents and skills.
Join the offline community on Discord

Hey @jaysermendez
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Hi, @jaysermendez,

Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork.

Knacksteem rewards individuals with unique talents and skills.

Join the offline community on Discord

Hey @jaysermendez}!

Thanks for contributing on KnackSteem.
Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork.

Knacksteem rewards individuals with unique talents and skills.


Join the offline community on Discord
Knacksteem Logo

Hey @jaysermendez!

Thanks for contributing on KnackSteem.
Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork.

Knacksteem rewards individuals with unique talents and skills.


Join the offline community on Discord
Knacksteem Logo

Hey @jaysermendez!

Thanks for contributing on KnackSteem.
Knacksteem appreciates your contribution and has rewarded you with an upvote for your hardwork.

Knacksteem rewards individuals with unique talents and skills.


Join the offline community on Discord
Knacksteem Logo