Skip to content

Instantly share code, notes, and snippets.

@jsullivan5
Created August 21, 2017 09:09
Show Gist options
  • Save jsullivan5/79cdcb95e6a3095bcc8f8b495b0d4c2a to your computer and use it in GitHub Desktop.
Save jsullivan5/79cdcb95e6a3095bcc8f8b495b0d4c2a to your computer and use it in GitHub Desktop.

server.js

//importing express into the project
const express = require('express');

//importing body parser into the file, allows express to automatically decode request and response bodies
const bodyParser = require('body-parser');

//router is used to make modular server architectures
const router = require('./server/router');

// importing path into the project, allows express to use nested file paths
const path = require('path');

// importing the controller that has all of the route callbacks
const controller = require('./server/controller');

// instantiating an express application
const app = express();

// the application will serve up files to a certain environment (test, development, staging) and default to localhost:3000
app.set('port', process.env.PORT || 3000);

// creating an internal name for the project and attaching it the locals object
app.locals.title = 'Jet Fuel';

// express is serving up static assets and declaring a path to the public folder in the root directory
app.use(express.static(path.resolve(__dirname, './public')));

// body parser will return json data when interpreting request and response bodies
app.use(bodyParser.json());

// the midddleware will parse the body into key value pairs, extended true means those values can be any datatype
app.use(bodyParser.urlencoded({ extended: true }));

// relative urls starting with /api will be passed to the router
app.use('/api', router);

// on homepage app will serve the html file
app.get('/', (req, res) => {
  res.status(200).sendFile(path.join(__dirname, 'public/index.html'))
})

// on realtive url path app will call the redirect function from controller and redirect to long url
app.get('/*/:charHash', controller.redirectUrl)

// app is listening for events on previously sti[ulated port and alerting in the console what port it is listening to.
app.listen(app.get('port'), () => {
  console.log(`${app.locals.title} is running on http://localhost:${app.get('port')}.`);
});

// exporting the app for testing suite
module.exports = app;

#router.js

// importing express to use router
const express = require('express');

//importing router and linking it to express
const router = express.Router();

// importing controller to use callbacks
const controller = require('./controller');

// router listening for GET requests on /api/v1/folders
router.get('/v1/folders', controller.getFolders);


// router listening for POST requests on /api/v1/folders
router.post('/v1/folders', controller.newFolder);


// router listening for GET requests on /api//v1/folders/:id/links
router.get('/v1/folders/:id/links', controller.getFolderLinks);

// router listening for POST requests on /api//v1/links
router.post('/v1/links', controller.createNewLink);

// exporting router to link in app
module.exports = router;

controller.js

// setting environment variable for knex database, defaulting to development
const environment = process.env.NODE_ENV || 'development';

// establishing stipulated environment settings to use with database
const configuration = require('../knexfile')[environment];

// importing the database with its settings fro the particular environment
const database = require('knex')(configuration);

// importing node package to make short hashes of strings for shortened urls
const shortHash = require('short-hash');

// creating a function to get folders from database
const getFolders = (request, response) => {

//selecting all the folders from the folders table in DB
  database('folders').select('*')
  
  // promise is returned and folders are sent to client with a 200 OK status
    .then(folders => {
      response.status(200).json(folders);
    })
    
    //any errors result in a 500 internal server error and the error is sent to client
    .catch(error => {
      response.status(500).json({ error });
    })
}

// function expression to create new folder in the database
const newFolder = (request, response) => {

  // request body is assigned to newFolder variable
  const newFolder = request.body;
  
  // keys in requst body are checked for required data and rejected if not present with a 422 unprocessable entity
  for (let requiredParameter of ['name']) {
    if (!newFolder[requiredParameter]) {
      return response.status(422).json({
        error: `Missing required parameter ${requiredParameter}`
      });
    }
  }

  // folders table is selected in DB and request body is inserted in DB.  name and id are returned.
  database('folders').insert(newFolder, ['name', 'id'])
  
    // if successful promise is returned, 201 created status code is sent along with the name and id to client
    .then(folder => {
      console.log(folder)
      response.status(201).json({
        name: folder[0].name,
        id: folder[0].id })
    })
    
    // errors at any point give internal server error to client
    .catch(error => {
      response.status(500).json({ error })
    });
}

// function expression to get links associated with a folder
const getFolderLinks = (request, response) => {

  // links table is selected and narrowed down to links associated with the requested folder
  database('links').where('folder_id', request.params.id).select()
  
    // if success, status 200 OK is sent to client along with the links that were requested
    .then(links => {
      response.status(200).json(links);
    })
    
    // if problem with request, internal server error 500 is sent to client
    .catch(error => {
      response.status(500).json({ error })
    })
}


// function expression to create new link in DB
const createNewLink = (req, res) => {

  // short URL is made using shorthash, interpolated into a quazi url
  const shortURL = `http://jt.fl/${shortHash(req.body.long_URL)}`

  // short url is injected intp request body
  const formattedData = Object.assign(req.body, {
    short_URL: shortURL
  });
  
  // request is checked for required parameters
  for (let requiredParams of ['long_URL', 'short_URL', 'folder_id', 'description']) {
  
    // if data is missing, return status 422 unprocessable entity to client with message
    if (!formattedData[requiredParams]) {
      return res.status(422).json({
        error: `Missing required parameter ${requiredParams}.`
      })
    }
  }
  
  // links table selected in DB and request body is inserted.  The entire request body is returned
  database('links').insert(formattedData, '*')
  
    //success sends status 201 created to client
    .then(link => {
      res.status(201).json(link[0])
    })
    
    // failure sends internal server error 500 to client
    .catch(error => res.status(500).json({ error }))
}

//function expression to redirect client to long url
const redirectUrl = (request, response) => {

  //links table selected in DB and matched to data that have the same short URL
  database('links').where({
    short_URL: `http://jt.fl/${request.params.charHash}`
  })
  
    // long url is taken from matched data and returned
    .select('long_URL')
    
      // if successful, client is redirected to url
      .then(data => {
        response.redirect(`${data[0].long_URL}`);
      })
      
      //failure and 404 not found is sent to client
      .catch(error => {
        response.status(404).json({ error });
      });
}

module.exports = {
  getFolders: getFolders,
  newFolder: newFolder,
  getFolderLinks: getFolderLinks,
  createNewLink: createNewLink,
  redirectUrl: redirectUrl
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment