Home Reference Source Repository

src/controllers/BeerController.js

// Import the neccesary modules.
import { Types } from 'mongoose';

import Beer from '../models/beer';
import User from '../models/user';
import { pageSize } from '../config/constants';

/** Class for CRUD operations for beers. */
export default class BeerController {

  /** Create a new BeerController object */
  constructor() {
    /**
     * The projection used for getting multiple beers.
     * @type {Object}
     */
    BeerController._projection = {
      name: 1,
      style: 1,
      type: 1,
      abv: 1,
      ebu: 1
    };
  }

  /**
   * Attatch a beer object to the req.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @param {String} id - The id of the beer to attach.
   * @returns {Response} - A request with a beer object attatched.
   */
  preLoadBeer(req, res, next, id) {
    return Beer.findOne({
      _id: id
    }).then(beer => {
      if (!beer) return next(new Error('Can\'t find beer!'));
      req.beer = beer;
      return next();
    }).catch(err => res.json(err));
  }

  /**
   * Get a list of beers based on the page number and search query.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @returns {JSON} - Get a list of beers.
   */
  getBeers(req, res, next) {
    const query = {};
    const data = req.query;
    const page = data.page ? data.page - 1 : 0;
    const offset = page * pageSize;

    data.order = data.order ? parseInt(data.order, 10) : -1;

    let sort = {
      name: data.order
    };

    if (data.search) {
      const words = data.search.split(' ');
      let regex = '^';

      for (const w in words) {
        words[w] = words[w].replace(/[^a-zA-Z0-9]/g, '');
        regex += `(?=.*\\b${RegExp.escape(words[w].toLowerCase())}\\b)`;
      }

      query.name = {
        $regex: new RegExp(`${regex}.*`),
        $options: 'gi'
      };
    }

    if (data.sort) {
      if (data.sort.match(/name/i)) sort = {
        name: data.order
      };
      if (data.sort.match(/style/i)) sort = {
        style: data.order
      };
      if (data.sort.match(/type/i)) sort = {
        type: data.order
      };
      if (data.sort.match(/abv/i)) sort = {
        abv: data.order
      };
      if (data.sort.match(/ebu/i)) sort = {
        ebu: data.order
      };
    }

    return Beer.aggregate([{
      $sort: sort
    }, {
      $match: query
    }, {
      $project: BeerController._projection
    }, {
      $skip: offset
    }, {
      $limit: pageSize
    }]).exec()
      .then(docs => res.json(docs))
      .catch(err => next(err));
  }

  /**
   * Get a beer based on the id.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @returns {JSON} - A beer based on the given id.
   */
  getBeer(req, res, next) {
    return Beer.findOne({
      _id: new Types.ObjectId(req.params.beer)
    }).exec()
      .then(beer => res.json(beer))
      .catch(err => next(err));
  }

  /**
   * Create update an existing beer or create a new beer if not exists.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @returns {JSON} - The created or updated beer.
   */
  createBeer(req, res, next) {
    const { name, style, type, abv, ebu, thumbnail } = req.body;

    const beer = new Beer({
      name,
      style,
      type,
      abv,
      ebu,
      thumbnail
    })
    beer.user = req.payload.username;
    return beer.save(err => {
      if (err) return next(err);
      return res.json(beer)
    });
  }

  /**
   * Create update an existing beer or create a new beer if not exists.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @returns {JSON} - The updated beer.
   */
  updateBeer(req, res, next) {
    const { name, style, type, abv, ebu, thumbnail } = req.body;

    return Beer.findOneAndUpdate({
      _id: new Types.ObjectId(req.params.beer),
      user: req.payload.username
    }, {
      name,
      style,
      type,
      abv,
      ebu,
      thumbnail
    }, {
      new: true
    }).exec()
      .then(beer => res.json(beer))
      .catch(err => next(err));
  }

  /**
   * Delete an beer based on the id.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @returns {JSON} - The deleted beer.
   */
  deleteBeer(req, res, next) {
    return Beer.findOneAndRemove({
      _id: new Types.ObjectId(req.params.beer),
      user: req.payload.username
    }).exec()
      .then(beer => res.json(beer))
      .catch(err => next(err))
  }

  /**
   * Upvote or downvote an beer based on the id.
   * @param {Request} req - The express request object.
   * @param {Response} res - The express response object.
   * @param {function} next - The next function for Express.
   * @returns {JSON} - The voted beer.
   */
  voteBeer(req, res, next) {
    return User.findOne({
      username: req.payload.username
    }).exec().then(user => {
      const beer = user.beers.find(el => el === req.beer.name);
      if (beer) {
        return user.removeBeer(req.beer)
          .then(() => req.beer.downvote())
          .then(newBeer => res.json(newBeer));
      }

      return user.addBeer(req.beer)
        .then(() => req.beer.upvote())
        .then(newBeer => res.json(newBeer));
    }).catch(err => next(err));
  }

}