Skip to content
Permalink
Browse files
added stub functions
  • Loading branch information
aa7401 committed Mar 16, 2019
1 parent be4b0b3 commit e10819513bec8055ba17e2fee2ff9114d3f3954a
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 14 deletions.
@@ -7,6 +7,7 @@ data/
coverage/
docs/
sessions/
screenshots/*

# sqlite databases
# *.db
@@ -0,0 +1,38 @@

# Code Quality

In this worksheet you will be applying a range of techniques to improve the quality of your code.

## 1 Modularisation

The first step you will need to do is to split your code up to make it easier to understand. Take a look at the end of the `index.js` routes file.

Start by updating your routes file by copying over the `modules/accounts.js` file from the lab materials and making sure you import it into your `index.js` file by adding the following statement just below where all the other modules are imported:

```javascript
const accounts = require('modules/accounts')
```

This loads the module into a constant called `accounts`.

Now locate the second `router.post('/login')` route (this is currently commented out). Comment out the one you have been using and uncomment this new shorter version. If you run your server and test it you will find the functionality is identical. Its time to understand how this new version works:

1. We start by storing the `request.body` data (the HTTP POST request body data) in an immutable variable called `body`.
2. Next we call the `checkCredentials()` function that we imported from the `accounts.js` module passing the username and password as parameters.
3. If this function does not throw an exception it means the credentials are valid so we set the cookie and redirect to the home page.
4. If either the username or password are invalid, the `checkCredentials()` function will throw an exception which will be handled by the `catch()` block. The error message will explain what us wrong so we pass this back to the login page.

Now we need to look at the `accounts.js` module. This implements some very important concepts that you will need to understand and apply to your assignment.

1.

### 1.1 Test Your Understanding

To check you understand how to use modules you are now expected to move more of the functionality from the `index.js` file into this separate module. To help you with this you will find stub functions that only require the functionality to be added! You will be modifying the functions below the `/* --- STUB FUNCTIONS --- */` line.

1. Implement the `checkNoDuplicateUsername(username)` function to comply with the JSDoc comments, it should throw an exception if a duplicate user is found or `true` if not.
2. Implement the `saveImage()` function. This should check that the image is of the correct type then save it to the `avatars/` directory.
3. Now implement the `addUser()` function. This should make use of the functions you created in the first two tasks. It should check for duplicates before saving the image then it should encrypting the password and then saving the new record.
4. The final step is to comment out the `router.post('register')` route in `index.js` then create a replacement that makes use of the functions you added to the `accounts.js` module.

Now much of the business logic has been moved to the separate module, are there any module imports in `index.js` that are no longer needed? Locate these and delete.
@@ -0,0 +1,5 @@

# Automated Testing

In this worksheet you will learn the basics of using automated tests to improve the quality of your code.

File renamed without changes.
@@ -11,14 +11,17 @@ const Router = require('koa-router')
const views = require('koa-views')
const staticDir = require('koa-static')
const bodyParser = require('koa-bodyparser')
const koaBody = require('koa-body')({multipart: true, uploadDir: '.'})
const koaBody = require('koa-body')({multipart: true, uploadDir: '.'})
const session = require('koa-session')
const sqlite = require('sqlite-async')
const bcrypt = require('bcrypt-promise')
const fs = require('fs-extra')
const mime = require('mime-types')
//const jimp = require('jimp')

/* IMPORT CUSTOM MODULES */
const accounts = require('./modules/accounts')

const app = new Koa()
const router = new Router()

@@ -78,23 +81,34 @@ router.get('/login', async ctx => {
await ctx.render('login', data)
})

router.post('/login', async ctx => {
// router.post('/login', async ctx => {
// try {
// const body = ctx.request.body
// const db = await sqlite.open('./website.db')
// // DOES THE USERNAME EXIST?
// const records = await db.get(`SELECT count(id) AS count FROM users WHERE user="${body.user}";`)
// if(!records.count) return ctx.redirect('/login?msg=invalid%20username')
// const record = await db.get(`SELECT pass FROM users WHERE user = "${body.user}";`)
// await db.close()
// // DOES THE PASSWORD MATCH?
// const valid = await bcrypt.compare(body.pass, record.pass)
// if(valid === false) return ctx.redirect(`/login?user=${body.user}&msg=invalid%20password`)
// // WE HAVE A VALID USERNAME AND PASSWORD
// ctx.session.authorised = true
// return ctx.redirect('/?msg=you are now logged in...')
// } catch(err) {
// await ctx.render('error', {message: err.message})
// }
// })

router.post('/login', async ctx => { // 19 lines reduced to 10!
const body = ctx.request.body
try {
const body = ctx.request.body
const db = await sqlite.open('./website.db')
// DOES THE USERNAME EXIST?
const records = await db.get(`SELECT count(id) AS count FROM users WHERE user="${body.user}";`)
if(!records.count) return ctx.redirect('/login?msg=invalid%20username')
const record = await db.get(`SELECT pass FROM users WHERE user = "${body.user}";`)
await db.close()
// DOES THE PASSWORD MATCH?
const valid = await bcrypt.compare(body.pass, record.pass)
if(valid === false) return ctx.redirect(`/login?user=${body.user}&msg=invalid%20password`)
// WE HAVE A VALID USERNAME AND PASSWORD
await accounts.checkCredentials(body.user, body.pass)
ctx.session.authorised = true
return ctx.redirect('/?msg=you are now logged in...')
} catch(err) {
await ctx.render('error', {message: err.message})
return ctx.redirect(`/login?user=${body.user}&msg=${err.message}`)
}
})

@@ -0,0 +1,94 @@
#!/usr/bin/env node

/**
* Accounts module
* @module modules/accounts
*/

'use strict'

const sqlite = require('sqlite-async')
const bcrypt = require('bcrypt-promise')

/**
* This is a generic function that opens the database, executes a query,
* closes the database connection and returns the data.
* @param {String} query - The SQL statement to execute.
* @returns {Object} - the date returned by the query.
*/
async function runSQL(query) {
try {
const DBName = './website.db'
const db = await sqlite.open(DBName)
const data = await db.all(query)
await db.close()
if(data.length === 1) return data[0]
return data
} catch(err) {
throw err
}
}

module.exports.checkCredentials = async(username, password) => {
try {
const records = await runSQL(`SELECT count(id) AS count FROM users WHERE user="${username}";`)
if(!records.count) throw new Error('invalid username')
const record = await runSQL(`SELECT pass FROM users WHERE user = "${username}";`)
const valid = await bcrypt.compare(password, record.pass)
if(valid === false) throw new Error(`invalid password`)
return true
} catch(err) {
throw err
}
}

/* ----------------------------- STUB FUNCTIONS ----------------------------- */

/**
* This function checks the database to see if a username already exists in
* the database. If it detects a duplicate it throws an exception.
* @param {String} username - The username to check.
* @returns {boolean} - returns true if the username does not exist.
* @throws {Error} - throws an error if the username already exists.
*/
async function checkNoDuplicateUsername(username) {
try {
const sql = `SELECT count(id) AS count FROM users WHERE user="${username}";`
const data = await runSQL(sql)
if(data.count) throw new Error('Username already Exists!')
return true
} catch(err) {
throw err
}
}

/**
* This function takes data from an uploaded image and saves it to the `avatars` directory. The file name will be the username.
* @param {String} path - the location of the uploaded image
* @param {String} mimeType - the mime type of the uploaded file.
* @returns {boolean} - returns true if the image is valid and is saved.
* @throws {TypeError} - throws an error if the file is not a png or jpg image.
*/
async function saveImage(path, mimetype) {
return true
}

/**
* This function adds new users to the database.
* @param {String} username - The username to to add.
* @param {String} password - The password to add.
* @returns {boolean} - returns true if the username does not exist.
* @throws {Error} - throws an error if the new user account has been created.
*/
module.exports.addUser = async(username, password) => {
try {
await checkNoDuplicateUsername(username)
const saltRounds = 10
password = await bcrypt.hash(password, saltRounds)
const sql = `INSERT INTO users(user, pass) VALUES("${username}", "${password}")`
await runSQL(sql)
return true
} catch(err) {
throw err
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e108195

Please sign in to comment.