Merge pull request #2 from adrianObel/feature/v2

Feature/v2
This commit is contained in:
Adrian Obelmejias
2016-03-19 11:34:44 -04:00
11 changed files with 309 additions and 178 deletions
+2 -2
View File
@@ -27,8 +27,8 @@ require('../config/passport')
app.use(passport.initialize())
app.use(passport.session())
const router = require('../src/controllers')
router(app)
const modules = require('../src/modules')
modules(app)
app.listen(config.port, () => {
console.log(`Server started on ${config.port}`)
-14
View File
@@ -1,14 +0,0 @@
import glob from 'glob'
exports = module.exports = function Controllers(app) {
glob(`${__dirname}/*.js`, { ignore: '**/index.js' }, (err, matches) => {
if (err) { throw err }
matches.forEach((file) => {
const controller = require(file).default
app
.use(controller.routes())
.use(controller.allowedMethods())
})
})
}
+8
View File
@@ -1,5 +1,7 @@
import mongoose from 'mongoose'
import bcrypt from 'bcrypt'
import config from '../../config'
import jwt from 'jsonwebtoken'
const User = new mongoose.Schema({
type: { type: String, default: 'User' },
@@ -47,4 +49,10 @@ User.methods.validatePassword = function validatePassword(password) {
})
}
User.methods.generateToken = function generateToken() {
const user = this
return jwt.sign({ id: user.id }, config.token)
}
export default mongoose.model('user', User)
@@ -1,9 +1,6 @@
import Router from 'koa-router'
import passport from 'koa-passport'
import jwt from 'jsonwebtoken'
import config from '../../config'
const router = new Router({ prefix: '/auth' })
import config from '../../../config'
/**
* @apiDefine TokenError
@@ -55,8 +52,9 @@ const router = new Router({ prefix: '/auth' })
* "error": "Unauthorized"
* }
*/
router.post('/', async (ctx, next) =>
passport.authenticate('local', (user) => {
export async function authUser(ctx, next) {
return passport.authenticate('local', (user) => {
if (!user) {
ctx.throw(401)
}
@@ -73,6 +71,4 @@ router.post('/', async (ctx, next) =>
user: response
}
})(ctx, next)
)
export default router
}
+13
View File
@@ -0,0 +1,13 @@
import * as auth from './controller'
export const baseUrl = '/auth'
export default [
{
method: 'POST',
route: '/',
handlers: [
auth.authUser
]
}
]
+30
View File
@@ -0,0 +1,30 @@
import glob from 'glob'
import Router from 'koa-router'
exports = module.exports = function initModules(app) {
glob(`${__dirname}/*`, { ignore: '**/index.js' }, (err, matches) => {
if (err) { throw err }
matches.forEach((mod) => {
const router = require(`${mod}/router`)
const routes = router.default
const baseUrl = router.baseUrl
const instance = new Router({ prefix: baseUrl })
routes.forEach((config) => {
const {
method = '',
route = '',
handlers = []
} = config
instance[method.toLowerCase()](route, ...handlers)
app
.use(instance.routes())
.use(instance.allowedMethods())
})
})
})
}
@@ -1,92 +1,4 @@
import Router from 'koa-router'
import User from '../models/users'
import config from '../../config'
import jwt from 'jsonwebtoken'
import { ensureUser } from '../middleware/validators'
const router = new Router({ prefix: '/users' })
/**
* @api {get} /users Get all users
* @apiPermission user
* @apiVersion 1.0.0
* @apiName GetUsers
* @apiGroup Users
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5000/users
*
* @apiSuccess {Object[]} users Array of user objects
* @apiSuccess {ObjectId} users._id User id
* @apiSuccess {String} users.name User name
* @apiSuccess {String} users.username User username
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "users": [{
* "_id": "56bd1da600a526986cf65c80"
* "name": "John Doe"
* "username": "johndoe"
* }]
* }
*
* @apiUse TokenError
*/
router.get('/',
ensureUser,
async (ctx) => {
const users = await User.find({}, '-password -salt')
ctx.body = users
}
)
/**
* @api {get} /users/:id Get user by id
* @apiPermission user
* @apiVersion 1.0.0
* @apiName GetUser
* @apiGroup Users
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5000/users/56bd1da600a526986cf65c80
*
* @apiSuccess {Object} users User object
* @apiSuccess {ObjectId} users._id User id
* @apiSuccess {String} users.name User name
* @apiSuccess {String} users.username User username
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "user": {
* "_id": "56bd1da600a526986cf65c80"
* "name": "John Doe"
* "username": "johndoe"
* }
* }
*
* @apiUse TokenError
*/
router.get('/:id',
ensureUser,
async (ctx) => {
try {
const user = await User.findById(ctx.params.id, '-password -salt')
if (!user) {
ctx.throw(404)
}
ctx.body = user
} catch (err) {
if (err === 404 || err.name === 'CastError') {
ctx.throw(404)
}
ctx.throw(500)
}
}
)
import User from '../../models/users'
/**
* @api {post} /users Create a new user
@@ -126,27 +38,105 @@ router.get('/:id',
* "error": "Unprocessable Entity"
* }
*/
router.post('/',
async (ctx) => {
const user = new User(ctx.request.body.user)
try {
await user.save()
const token = jwt.sign({ id: user.id }, config.token)
const response = user.toJSON()
delete response.password
delete response.salt
ctx.body = {
user: response,
token
}
} catch (err) {
ctx.throw(422, err)
}
export async function createUser(ctx) {
const user = new User(ctx.request.body.user)
try {
await user.save()
} catch (err) {
ctx.throw(422, err.message)
}
)
const token = user.generateToken()
const response = user.toJSON()
delete response.password
delete response.salt
ctx.body = {
user: response,
token
}
}
/**
* @api {get} /users Get all users
* @apiPermission user
* @apiVersion 1.0.0
* @apiName GetUsers
* @apiGroup Users
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5000/users
*
* @apiSuccess {Object[]} users Array of user objects
* @apiSuccess {ObjectId} users._id User id
* @apiSuccess {String} users.name User name
* @apiSuccess {String} users.username User username
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "users": [{
* "_id": "56bd1da600a526986cf65c80"
* "name": "John Doe"
* "username": "johndoe"
* }]
* }
*
* @apiUse TokenError
*/
export async function getUsers(ctx) {
const users = await User.find({}, '-password -salt')
ctx.body = { users }
}
/**
* @api {get} /users/:id Get user by id
* @apiPermission user
* @apiVersion 1.0.0
* @apiName GetUser
* @apiGroup Users
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5000/users/56bd1da600a526986cf65c80
*
* @apiSuccess {Object} users User object
* @apiSuccess {ObjectId} users._id User id
* @apiSuccess {String} users.name User name
* @apiSuccess {String} users.username User username
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "user": {
* "_id": "56bd1da600a526986cf65c80"
* "name": "John Doe"
* "username": "johndoe"
* }
* }
*
* @apiUse TokenError
*/
export async function getUser(ctx, next) {
try {
const user = await User.findById(ctx.params.id, '-password -salt')
if (!user) {
ctx.throw(404)
}
ctx.body = {
user
}
} catch (err) {
if (err === 404 || err.name === 'CastError') {
ctx.throw(404)
}
ctx.throw(500)
}
next()
}
/**
* @api {put} /users/:id Update a user
@@ -188,30 +178,17 @@ router.post('/',
*
* @apiUse TokenError
*/
router.put('/:id',
ensureUser,
async (ctx) => {
try {
const user = await User.findById(ctx.params.id, '-password -salt')
if (!user) {
ctx.throw(404)
}
export async function updateUser(ctx) {
const user = ctx.body.user
Object.assign(user, ctx.request.body.user)
Object.assign(user, ctx.request.body.user)
await user.save()
ctx.body = {
user
}
} catch (err) {
if (err === 404 || err.name === 'CastError') {
ctx.throw(404)
}
await user.save()
ctx.throw(500)
}
ctx.body = {
user
}
)
}
/**
* @api {delete} /users/:id Delete a user
@@ -228,31 +205,19 @@ router.put('/:id',
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "status": 200
* "success": true
* }
*
* @apiUse TokenError
*/
router.delete('/:id',
ensureUser,
async (ctx) => {
try {
const user = await User.findById(ctx.params.id)
if (!user) {
ctx.throw(404)
}
await user.remove()
export async function deleteUser(ctx) {
const user = ctx.body.user
ctx.body = 200
} catch (err) {
if (err === 404 || err.name === 'CastError') {
ctx.throw(404)
}
await user.remove()
ctx.throw(500)
}
ctx.status = 200
ctx.body = {
success: true
}
)
export default router
}
+48
View File
@@ -0,0 +1,48 @@
import { ensureUser } from '../../middleware/validators'
import * as user from './controller'
export const baseUrl = '/users'
export default [
{
method: 'POST',
route: '/',
handlers: [
user.createUser
]
},
{
method: 'GET',
route: '/',
handlers: [
ensureUser,
user.getUsers
]
},
{
method: 'GET',
route: '/:id',
handlers: [
ensureUser,
user.getUser
]
},
{
method: 'PUT',
route: '/:id',
handlers: [
ensureUser,
user.getUser,
user.updateUser
]
},
{
method: 'DELETE',
route: '/:id',
handlers: [
ensureUser,
user.getUser,
user.deleteUser
]
}
]
+51
View File
@@ -0,0 +1,51 @@
import app from '../bin/server'
import supertest from 'supertest'
import { expect, should } from 'chai'
import { cleanDb, authUser } from './utils'
should()
const request = supertest.agent(app.listen())
const context = {}
describe('Auth', () => {
before((done) => {
cleanDb()
authUser(request, (err, { user, token }) => {
if (err) { return done(err) }
context.user = user
context.token = token
done()
})
})
describe('POST /auth', () => {
it('should throw 401 if credentials are incorrect', (done) => {
request
.post('/auth')
.set('Accept', 'application/json')
.send({ username: 'supercoolname', password: 'wrongpassword' })
.expect(401, done)
})
it('should auth user', (done) => {
request
.post('/auth')
.set('Accept', 'application/json')
.send({ username: 'test', password: 'pass' })
.expect(200, (err, res) => {
if (err) { return done(err) }
res.body.user.should.have.property('username')
res.body.user.username.should.equal('test')
expect(res.body.user.password).to.not.exist
expect(res.body.user.salt).to.not.exist
context.user = res.body.user
context.token = res.body.token
done()
})
})
})
})
+19 -2
View File
@@ -56,7 +56,15 @@ describe('Users', () => {
request
.get(`/users?token=${token}`)
.set('Accept', 'application/json')
.expect(200, done)
.expect(200, (err, res) => {
if (err) { return done(err) }
res.body.should.have.property('users')
res.body.users.should.have.length(1)
done()
})
})
})
@@ -85,7 +93,16 @@ describe('Users', () => {
request
.get(`/users/${_id}?token=${token}`)
.set('Accept', 'application/json')
.expect(200, done)
.expect(200, (err, res) => {
if (err) { return done(err) }
res.body.should.have.property('user')
expect(res.body.user.password).to.not.exist
expect(res.body.user.salt).to.not.exist
done()
})
})
})
+19 -2
View File
@@ -1,7 +1,24 @@
import mongoose from 'mongoose'
export function cleanDb () {
export function cleanDb() {
for (const collection in mongoose.connection.collections) {
mongoose.connection.collections[collection].remove();
if (mongoose.connection.collections.hasOwnProperty(collection)) {
mongoose.connection.collections[collection].remove()
}
}
}
export function authUser(agent, callback) {
agent
.post('/users')
.set('Accept', 'application/json')
.send({ user: { username: 'test', password: 'pass' } })
.end((err, res) => {
if (err) { return callback(err) }
callback(null, {
user: res.body.user,
token: res.body.token
})
})
}