Ver código fonte

Add poll voting route

sbkwgh 8 anos atrás
pai
commit
64170fba6c
5 arquivos alterados com 194 adições e 5 exclusões
  1. 6 0
      models/poll_answer.js
  2. 2 3
      models/poll_question.js
  3. 15 0
      models/poll_vote.js
  4. 45 1
      routes/poll.js
  5. 126 1
      test/poll.js

+ 6 - 0
models/poll_answer.js

@@ -17,6 +17,12 @@ module.exports = (sequelize, DataTypes) => {
 				}
 			}
 		}
+	}, {
+		classMethods: {
+			associate (models) {
+				PollAnswer.hasMany(models.PollVote)
+			}
+		}
 	})
 
 	return PollAnswer

+ 2 - 3
models/poll_question.js

@@ -21,9 +21,8 @@ module.exports = (sequelize, DataTypes) => {
 		classMethods: {
 			associate (models) {
 				PollQuestion.belongsTo(models.User)
-				PollQuestion.hasMany(models.PollAnswer, {
-					foreignKey: { name: 'question_id' }
-				})
+				PollQuestion.hasMany(models.PollAnswer)
+				PollQuestion.hasMany(models.PollVote)
 			}
 		}
 	})

+ 15 - 0
models/poll_vote.js

@@ -0,0 +1,15 @@
+module.exports = (sequelize, DataTypes) => {
+	let Sequelize = sequelize.Sequelize
+
+	let PollVote = sequelize.define('PollVote', {}, {
+		classMethods: {
+			associate (models) {
+				PollVote.belongsTo(models.PollAnswer)
+				PollVote.belongsTo(models.PollQuestion)
+				PollVote.belongsTo(models.User)
+			}
+		}
+	})
+
+	return PollVote
+}

+ 45 - 1
routes/poll.js

@@ -1,7 +1,7 @@
 let express = require('express')
 let router = express.Router()
 
-let { PollAnswer, PollQuestion, User, Sequelize } = require('../models')
+let { PollAnswer, PollQuestion, PollVote, User, Sequelize } = require('../models')
 const Errors = require('../lib/errors')
 
 router.all('*', (req, res, next) => {
@@ -64,4 +64,48 @@ router.post('/', async (req, res) => {
 	}
 })
 
+router.post('/:id', async (req, res) => {
+	try {
+		let previousVote = await PollVote.findOne({
+			where: { PollQuestionId: req.params.id, UserId: req.session.UserId }
+		})
+		if(previousVote) throw Errors.sequelizeValidation(Sequelize, {
+			error: 'you cannot vote twice',
+			value: req.params.id
+		})
+
+		let poll = await PollQuestion.findById(req.params.id, {
+			include: [PollAnswer]
+		})
+		if(!poll) throw Errors.sequelizeValidation(Sequelize, {
+			error: 'invalid poll id',
+			value: req.params.id
+		})
+
+		let pollAnswer = poll.PollAnswers.find(a => a.answer === req.body.answer)
+		if(!pollAnswer) throw Errors.sequelizeValidation(Sequelize, {
+			error: 'invalid answer',
+			value: req.body.answer
+		})
+
+		let pollVote = await PollVote.create({ UserId: req.session.UserId })
+		await pollVote.setPollQuestion(poll)
+		await pollVote.setPollAnswer(pollAnswer)
+
+		res.json(pollVote.toJSON())
+	} catch (e) {
+		if(e instanceof Sequelize.ValidationError) {
+			res.status(400)
+			res.json(e)
+		} else {
+			console.log(e)
+
+			res.status(500)
+			res.json({
+				errors: [Errors.unknown]
+			})
+		}
+	}
+})
+
 module.exports = router

+ 126 - 1
test/poll.js

@@ -4,7 +4,7 @@ let chai = require('chai')
 let server = require('../server')
 let should = chai.should()
 
-let { sequelize, PollQuestion, PollAnswer, User } = require('../models')
+let { sequelize, PollQuestion, PollAnswer, PollVote, User } = require('../models')
 
 const Errors = require('../lib/errors.js')
 
@@ -191,6 +191,131 @@ describe('Poll', () => {
 					})
 			})
 		})
+
+		describe('POST /poll/:id', () => {
+			let id, id2
+
+			before(async () => {
+				try {
+					let res = await user1
+						.post('/api/v1/poll')
+						.set('content-type', 'application/json')
+						.send({
+							question: 'Poll question',
+							answers: ['poll answer 1', 'poll answer 2', 'poll answer 3']
+						})
+
+					id = res.body.id
+
+					let res2 = await user1
+						.post('/api/v1/poll')
+						.set('content-type', 'application/json')
+						.send({
+							question: 'Poll question',
+							answers: ['poll answer 1', 'poll answer 2', 'poll answer 3']
+						})
+
+					id2 = res2.body.id
+
+					return true
+				} catch (e) {
+					return e
+				}
+			})
+
+			it('should add a vote to the poll', async () => {
+				let res = await user1
+					.post('/api/v1/poll/' + id)
+					.set('content-type', 'application/json')
+					.send({ answer: 'poll answer 1' })
+
+				res.should.be.json
+				res.should.have.status(200)
+
+				let answer = await PollAnswer.findOne({
+					where: {
+						answer: 'poll answer 1'
+					}
+				})
+
+				let vote = await PollVote.findById(res.body.id)
+				vote.should.not.be.null
+				vote.should.have.property('PollQuestionId', id)
+				vote.should.have.property('PollAnswerId', answer.id)
+			})
+			it('should return an error if voting twice', done => {
+				user1
+					.post('/api/v1/poll/' + id)
+					.set('content-type', 'application/json')
+					.send({ answer: 'poll answer 2' })
+					.end((err, res) => {
+						res.should.have.status(400)
+						res.body.errors.should.contain.something.that.has.property(
+							'message',
+							'you cannot vote twice'
+						)
+
+						done()
+					})
+			})
+			it('should return an error if invalid id', done => {
+				user1
+					.post('/api/v1/poll/404')
+					.set('content-type', 'application/json')
+					.send({ answer: 'poll answer 1' })
+					.end((err, res) => {
+						res.should.have.status(400)
+						res.body.errors.should.contain.something.that.has.property(
+							'message',
+							'invalid poll id'
+						)
+
+						done()
+					})
+			})
+			it('should return an error if missing answer', done => {
+				user1
+					.post('/api/v1/poll/' + id2)
+					.set('content-type', 'application/json')
+					.send()
+					.end((err, res) => {
+						res.should.have.status(400)
+						res.body.errors.should.contain.something.that.has.property(
+							'message',
+							'invalid answer'
+						)
+
+						done()
+					})
+			})
+			it('should return an error if answer is invalid', done => {
+				user1
+					.post('/api/v1/poll/' + id2)
+					.set('content-type', 'application/json')
+					.send({ answer: 'not an option' })
+					.end((err, res) => {
+						res.should.have.status(400)
+						res.body.errors.should.contain.something.that.has.property(
+							'message',
+							'invalid answer'
+						)
+
+						done()
+					})
+			})
+			it('should return an error if not logged in', done => {
+				chai.request(server)
+					.post('/api/v1/poll/' + id)
+					.set('content-type', 'application/json')
+					.send({ answer: 'poll answer 1' })
+					.end((err, res) => {
+						res.should.have.status(401)
+						res.body.errors.should.contain.something.which.deep.equals(Errors.requestNotAuthorized)
+					})
+
+					done()
+			})
+		})
 	})
 
 	after(() => sequelize.sync({ force: true }))