diff --git a/app.js b/app.js index 132954d..9f252e5 100644 --- a/app.js +++ b/app.js @@ -10,6 +10,12 @@ require('babel-polyfill'); if (env === 'development') { require('babel-register'); } const app = require(src).default; -app.listen(port); + +//Here we're assigning the server to a variable because +//we're going to want to manually rip down the server in testing +const server = app.listen(port); console.log('Server running at ' + port); -console.log("Running in " + process.env.NODE_ENV + " v" + process.env.npm_package_version); \ No newline at end of file +console.log("Running in " + process.env.NODE_ENV + " v" + process.env.npm_package_version); + +//Exporting the actual server here for testing availability +module.exports = {server: server} \ No newline at end of file diff --git a/knexfile.js b/knexfile.js new file mode 100644 index 0000000..4cf807b --- /dev/null +++ b/knexfile.js @@ -0,0 +1,23 @@ +//This knexfile is just used for migrations. The actual +//db object is built in ./src/db/db and is used throughout the app. + +require('dotenv').config(); + +module.exports = { + + client: 'mysql', + connection: { + host: process.env.DB_HOST, + port: process.env.DB_PORT, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + }, + migrations: { + directory: './src/db/migrations', + }, + seeds: { + directory: './src/db/seeds/dev', + }, + +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0bab2b8..2e26415 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "koa-vue-notes-api", - "version": "1.0.3", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1980,6 +1980,11 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "faker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", + "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=" + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", diff --git a/package.json b/package.json index 1545532..cbec662 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,16 @@ { "name": "koa-vue-notes-api", - "version": "1.0.4", + "version": "1.1.1", "description": "A SPA using Koa as the backend and Vue as the frontend.", "author": "John Datserakis", "private": false, "scripts": { - "start": "node app.js", - "watch": "nodemon --exec npm run start", - "test": "jest --forceExit", - "build": "babel src -d build && npm run pretty", - "pretty": "prettier --write --print-width 80 --single-quote --trailing-comma es5 --tab-width 4 --no-semi 'src/**/*.js' 'models/**/*.js' 'middleware/**/*.js' 'config/**/*.js'", - "start-production": "pm2 start ecosystem.json", - "clean": "npm run clean:dist && npm run clean:tmp" + "watch": "NODE_ENV=development nodemon app.js --exec", + "test": "NODE_ENV=testing jest -forceExit", + "build": "NODE_ENV=production babel src -d build && npm run pretty", + "pretty": "prettier --write --print-width 80 --single-quote --trailing-comma es5 --tab-width 4 --no-semi 'src/**/*.js'", + "start-production": "NODE_ENV=production pm2 start ecosystem.json", + "clean": "npm cache clean --force" }, "license": "MIT", "homepage": "https://github.com/OrKoN/koa2-example-app#readme", @@ -21,6 +20,7 @@ "bcrypt": "^1.0.2", "date-fns": "^1.28.5", "dotenv": "^4.0.0", + "faker": "^4.1.0", "fs-extra": "^4.0.0", "install": "^0.10.1", "ioredis": "^3.1.1", diff --git a/readme.md b/readme.md index 230d983..e2925e3 100644 --- a/readme.md +++ b/readme.md @@ -28,7 +28,9 @@ This is a simple SPA built using [Koa](http://koajs.com/) (2.3) as the backend a - Prettier - Babel - PM2 -- MySQL with Knex +- MySQL +- Knex with migrations and seeds +- Faker - log4js - And more... @@ -49,6 +51,25 @@ npm run start-production # run prettier on the project npm run pretty + +# knex migration examples +# (from command line in root directory with knex installed globally) +# (*make* commands are just here to show syntax) + +# make migration +knex migrate:make create_users_table + +# migrate latest +knex migrate:latest + +# rollback +knex migrate:rollback + +# make seed +knex seed:make seed_users + +# run all seeds +knex seed:run ``` ## General Information @@ -59,9 +80,9 @@ I've liberally commented the code and tried to balance the project in a way that Having used mainly PHP for the backend in the past - I am very glad I checked out Koa as I think it is absolutely awesome in the way it handles the server code. Same thing with Vue - I've used mainly jQuery in the past - albeit with the really structured Revealing-Module-Pattern - and using Vue was such a pleasure. You can really tell right away what kind of power a well-structured library can give you. -You'll need to create a `.env` file and place it in the root of your directory. Take a look at `example.env` and add your information as needed. For `JWT_ACCESS_TOKEN_EXPIRATION_TIME` you can set it to 5m, 5w, 5d etc. +You'll need to create a `.env` file and place it in the root of your directory. Take a look at `example.env` and add your information as needed. For `JWT_ACCESS_TOKEN_EXPIRATION_TIME` you can set it to 5m, 5w, 5d etc - although 5m is what I'm using at the moment. Note - we don't set the NODE_ENV variable in the `.env` - we set it in the npm scripts. This lets us specifically set different environments as needed. -This project only responds and listens in json. Keep that in mind when send requests through Postman or your frontend. +This project only responds and listens in JSON. Keep that in mind when send requests through Postman or your frontend. ### User Authentication Process diff --git a/src/db/db.js b/src/db/db.js index 131a0fe..a59644a 100644 --- a/src/db/db.js +++ b/src/db/db.js @@ -10,9 +10,16 @@ const config = { database: process.env.DB_DATABASE, }, migrations: { - directory: __dirname + '/src/db/migrations', + directory: './src/db/migrations', }, } +//Here we check to see if we are in testing mode. If we are, +//we set the database name to one with a '_tests' at the end, +//which we'll build and tear-down for each test. +if (process.env.NODE_ENV === 'testing') { + config.connection.database = process.env.DB_DATABASE + '_tests' +} + const db = knex(config) export default db diff --git a/src/db/seeds/dev/seed_notes.js b/src/db/seeds/dev/seed_notes.js new file mode 100644 index 0000000..fbed3b8 --- /dev/null +++ b/src/db/seeds/dev/seed_notes.js @@ -0,0 +1,22 @@ +const faker = require('faker'); + +exports.seed = async function(knex, Promise) { + + //Make 100 notes for 10 different users + let seedData = []; + for (let i = 0; i < 100; i++) { + let testNote = { + userId: faker.random.number({min: 1, max: 10}), + title: faker.lorem.sentence(), + content: faker.lorem.sentences(Math.floor(Math.random() * 10) + 1) + } + seedData.push(testNote) + } + + // Deletes ALL existing entries + await knex('notes').truncate() + + //Insert users + await knex('notes').insert(seedData); + +}; diff --git a/src/db/seeds/dev/seed_users.js b/src/db/seeds/dev/seed_users.js new file mode 100644 index 0000000..e98e691 --- /dev/null +++ b/src/db/seeds/dev/seed_users.js @@ -0,0 +1,35 @@ +const faker = require('faker'); +const bcrypt = require('bcrypt'); + +exports.seed = async function(knex, Promise) { + + //Make 10 users using faker. Note: we're also bcrypting + //the passwords to make it exactly like the real app. All their + //passwords will be 'secret' + let seedData = []; + for (let i = 0; i < 10; i++) { + let password = 'secret' + try { + password = await bcrypt.hash(password, 12) + } catch (error) { + throw new Error('PASSWORD_ENCRIPTION_ERROR') + } + + let testUser = { + token: faker.internet.password(), + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + username: faker.internet.userName(), + email: faker.internet.email(), + password: password, + } + seedData.push(testUser) + } + + // Deletes ALL existing entries + await knex('users').truncate() + + //Insert users + await knex('users').insert(seedData); + +}; diff --git a/tests/test.js b/tests/test.js index ec0fea2..d240536 100644 --- a/tests/test.js +++ b/tests/test.js @@ -1,8 +1,29 @@ -const axios = require('axios') -const app = require('../app') +// //This starts the app up +import {server} from '../app' + +//Set up axios a little bit +import axios from 'axios' const url = `http://localhost:4000` const request = axios.create({ baseURL: url }) +//Grab the db variable +import db from '../src/db/db' + +//Before each test we are going to rollback and +//migrate out database to its latest version +beforeEach(async () => { + await db.migrate.rollback() + await db.migrate.latest() + // await knex.seed.run() +}); + +//After all the tests are done we're going to close our server +//and rollback our database. +afterAll(async () => { + await db.migrate.rollback() + return server.close() +}); + it('returns homepage', async () => { expect.assertions(1) const response = await request.get('/')