Skip to content
Permalink
Browse files
completed the code quality lab
  • Loading branch information
aa7401 committed Mar 17, 2019
1 parent e108195 commit bd5b49f5847eb624540fdbed1dfbe8c8263802ca
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 47 deletions.
@@ -18,6 +18,7 @@
"complexity": ["error", 4],
"eol-last": "warn",
"eqeqeq": "error",
"func-call-spacing": ["error", "never"],
"global-require": "error",
"handle-callback-err": "warn",
"indent": [1, "tab", {"SwitchCase": 1}],
@@ -54,6 +55,7 @@
"prefer-const": 2,
"quotes": [1, "single"],
"semi": [1, "never"],
"space-before-blocks": ["error", "never"],
"space-before-function-paren": [2, "never"],
"strict": [2, "global"],
"yoda": 2
@@ -36,3 +36,59 @@ To check you understand how to use modules you are now expected to move more of
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.

## 2 Linting

When using a language as flexible as JavaScript which contains so many legal (but terrible) features, it is important to use a linter. This will check your code against a set of rules. These ensure:

1. You are not using what are considered bad language features.
2. You are implementing optional syntax (such as indentation and semicolons) in a consistent manner.
3. You are writing code that is easy to maintain.

If you look over both your `index.js` and `accounts.js` files you should be feeling pretty comfortable that you are already writing clean, consistent and maintainable code, lets see how good your code really is!

You should start by ensuring you have installed `eslint` which is considered the industry standard and that you have a copy of the approved configuration file `.eslintrc.json` in the root directory of your project. You can find this in the `TEACHING-MATERIALS` repository but make sure you take a copy of the latest version from the master repository!

Try running the linter on your `index.js` routes file:

```shell
$ node_modules/.bin/eslint index.js
```

You will see a list of issues that the linter has flagged in your code. Notice that some of these are flagged as errors (serious) and some as warnings (recommendations). Each message includes:

1. The line and comumn number where the error was found.
2. A description of the error.
3. The rule that is being broken.

The latter can be used to quickly look up the rules in the [comprehensive documentation](https://eslint.org/docs/rules/).

Instead of running separate checks on every file, we can specify the directory we want to check and it will automatically scan all the subdirectories. For example to scan all the files in the `modules/` directory we could run:

```shell
$ node_modules/.bin/eslint modules/
```

### 2.1 Test Your Understanding

1. How could you run the linter to scan _all_ the files in your project (HINT: you need to start scanning in the _current directory_)?
2. Now you should locate and fix all the errors and warnings in your code.
3. If you are using VS Code, install `eslint` globally and then install the [eslint extension](https://github.com/Microsoft/vscode-eslint). After restarting your editor you should see any errors and warnings flagged in the editor.

## 3 Documentation

In this third and last topic we will be using the [JSDoc](http://usejsdoc.org) tool to build a detailed code documentation website by extracting special comments inserted into our source code.

The default set of documentation tools provided in JSDoc are not suitable for documenting Koa routes and so we will be using a plugin called [jsdoc-route-plugin](https://www.npmjs.com/package/jsdoc-route-plugin). This should have been installed by the package manifest however you should check that you are using the current version of the `package.json` file and update if needed, rerunning the `npm install` command to ensure all packages are installed. You should also check that you have the latest version of the `jsdoc.conf` configuration file.

Now everything is installed we can run the `jsdoc` tool to generate our documentation.

```shell
$ node_modules/.bin/jsdoc
```

If you run this command you should see a new directory called `docs/` which will contain a `jsdoc/` directory. Inside this you will see some website files, opening the `index.html` file in your browser you should see the documentation pages for your website!

### 3.1 Test Your Understanding

You will probably have noticed that only a couple of the functions include complete JSDoc comments and so the documentation website is incomplete. Your task is to use the existing comments for guidance and complete the task of documenting your code. You will find the [JSDoc](http://usejsdoc.org) and [jsdoc-route-plugin](https://www.npmjs.com/package/jsdoc-route-plugin) documentation helpful.
@@ -1,7 +1,8 @@
#!/usr/bin/env node
/* eslint-disable max-lines */
/* eslint-disable max-statements */
/* eslint-disable max-lines-per-function */

/**
* Routes File
*/

'use strict'

@@ -35,6 +36,13 @@ app.use(views(`${__dirname}/views`, { extension: 'handlebars' }, {map: { handleb
const port = 8080
const saltRounds = 10

/**
* The secure home page.
*
* @name Home Page
* @route {GET} /
* @authentication This route requires cookie-based authentication.
*/
router.get('/', async ctx => {
try {
if(ctx.session.authorised !== true) return ctx.redirect('/login?msg=you need to log in')
@@ -46,8 +54,20 @@ router.get('/', async ctx => {
}
})

/**
* The user registration page.
*
* @name Register Page
* @route {GET} /register
*/
router.get('/register', async ctx => await ctx.render('register'))

/**
* The script to process new user registrations.
*
* @name Register Script
* @route {POST} /register
*/
router.post('/register', koaBody, async ctx => {
try {
const body = ctx.request.body
@@ -78,43 +98,43 @@ router.get('/login', async ctx => {
const data = {}
if(ctx.query.msg) data.msg = ctx.query.msg
if(ctx.query.user) data.user = ctx.query.user
await ctx.render('login', data)
await ctx.render('login', data)
})

// 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
router.post('/login', async ctx => {
try {
await accounts.checkCredentials(body.user, body.pass)
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) {
return ctx.redirect(`/login?user=${body.user}&msg=${err.message}`)
await ctx.render('error', {message: err.message})
}
})

// router.post('/login', async ctx => { // 19 lines reduced to 10!
// const body = ctx.request.body
// try {
// await accounts.checkCredentials(body.user, body.pass)
// ctx.session.authorised = true
// return ctx.redirect('/?msg=you are now logged in...')
// } catch(err) {
// return ctx.redirect(`/login?user=${body.user}&msg=${err.message}`)
// }
// })

router.get('/logout', async ctx => {
ctx.session.authorised = null
ctx.redirect('/')
ctx.session.authorised = null;
ctx.redirect('/')
})

app.use(router.routes())
@@ -124,4 +144,4 @@ module.exports = app.listen(port, async() => {
await db.run('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, pass TEXT);')
await db.close()
console.log(`listening on port ${port}`)
})
})
@@ -0,0 +1,22 @@

{
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc","closure"]
},
"source": {
"include": [ "." ],
"exclude": [ "node_modules" ],
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"plugins": ["jsdoc-route-plugin"],
"templates": {
"cleverLinks": false,
"monospaceLinks": false
},
"opts": {
"destination": "docs/jsdoc",
"recurse": true
}
}
@@ -17,28 +17,28 @@ const bcrypt = require('bcrypt-promise')
* @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()
try {
let 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
return data;
} catch(err) {
throw err
}
}

module.exports.checkCredentials = async(username, password) => {
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')
var 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`)
if(valid == false) throw new Error(`invalid password`)
return true
} catch(err) {
throw err
throw err
}
}

@@ -51,13 +51,13 @@ module.exports.checkCredentials = async(username, password) => {
* @returns {boolean} - returns true if the username does not exist.
* @throws {Error} - throws an error if the username already exists.
*/
async function checkNoDuplicateUsername(username) {
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!')
const num_records = await runSQL (sql)
if(num_records.count) throw new Error ('Username already Exists!')
return true
} catch(err) {
} catch (err) {
throw err
}
}
@@ -91,4 +91,4 @@ module.exports.addUser = async(username, password) => {
} catch(err) {
throw err
}
}
}
@@ -4,6 +4,8 @@
"description": "",
"main": "index.js",
"scripts": {
"jsdoc": "node_modules/.bin/jsdoc -c jsdoc.conf",
"linter": "node_modules/.bin/eslint .",
"test": "jest --coverage --detectOpenHandles"
},
"jest": {
@@ -28,7 +30,10 @@
"sqlite-async": "^1.0.11"
},
"devDependencies": {
"eslint": "^5.15.2",
"jest": "^24.1.0",
"jsdoc": "^3.5.5",
"jsdoc-route-plugin": "^0.1.0",
"puppeteer": "^1.12.2"
}
}

0 comments on commit bd5b49f

Please sign in to comment.