Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update question endpoint #577

Merged
merged 6 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion controllers/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ const createQuestion = async (req, res) => {
res.status(201).json(newQuestion)
}

const updateQuestion = async (req, res) => {
const { id } = req.params
const { id: currentUserId } = req.user
const data = req.body
await questionService.updateQuestion(id, currentUserId, data)

res.status(204).end()
}

module.exports = {
getQuestions,
createQuestion
createQuestion,
updateQuestion
}
60 changes: 58 additions & 2 deletions docs/questions/question.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ paths:
example:
items:
- _id: 64ca5914b57f2442403394a5
title: Is Assembly best programming language?
title: Is Assembly best programming language?
answers:
- text: Yes,
isCorrect: false
Expand Down Expand Up @@ -71,4 +71,60 @@ paths:
example:
status: 401
code: UNAUTHORIZED
message: The requested URL requires user authorization.
message: The requested URL requires user authorization.
patch:
security:
- bearerAuth: []
tags:
- Questions
summary: Update question by id.
description: Updates question by id.
produces:
- application/json
parameters:
- in: params
name: id
required: true
description: id of the question that needs to be updated
type: string
requestBody:
description: Data to update a question
content:
application/json:
schema:
$ref: '#/definitions/questionBody'
example:
title: WebAssembly
responses:
204:
description: No content
401:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/Error'
example:
status: 401
code: UNAUTHORIZED
message: The requested URL requires user authorization.
403:
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/Error'
example:
status: 403
code: FORBIDDEN
message: You do not have permission to perform this action.
404:
description: Not found
content:
application/json:
schema:
$ref: '#/components/Error'
example:
status: 404
code: NOT_FOUND
message: Question with the specified id was not found.
5 changes: 5 additions & 0 deletions routes/question.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
const router = require('express').Router()
const Question = require('~/models/question')

const questionController = require('~/controllers/question')
const asyncWrapper = require('~/middlewares/asyncWrapper')
const isEntityValid = require('~/middlewares/entityValidation')
const { authMiddleware, restrictTo } = require('~/middlewares/auth')

const {
roles: { TUTOR }
} = require('~/consts/auth')

router.use(authMiddleware)
const params = [{ model: Question, idName: 'id' }]

router.get('/', asyncWrapper(questionController.getQuestions))
router.use(restrictTo(TUTOR))
router.post('/', asyncWrapper(questionController.createQuestion))
router.patch('/:id', isEntityValid({ params }), asyncWrapper(questionController.updateQuestion))

module.exports = router
18 changes: 16 additions & 2 deletions services/question.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const Question = require('~/models/question')
const { createForbiddenError } = require('~/utils/errorsHelper')

const questionService = {
getQuestions: async (match, sort, skip = 0, limit = 10) => {
const items = await Question
.find(match)
const items = await Question.find(match)
.collation({ locale: 'en', strength: 1 })
.sort(sort)
.skip(skip)
Expand All @@ -24,6 +24,20 @@ const questionService = {
type,
author
})
},

updateQuestion: async (id, currentUserId, data) => {
const question = await Question.findById(id).exec()

const author = question.author.toString()

if (currentUserId !== author) {
throw createForbiddenError()
}
for (let field in data) {
question[field] = data[field]
}
await question.save()
}
}
module.exports = questionService
41 changes: 40 additions & 1 deletion test/integration/controllers/question.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { expectError } = require('~/test/helpers')
const { UNAUTHORIZED, FORBIDDEN } = require('~/consts/errors')
const testUserAuthentication = require('~/utils/testUserAuth')
const TokenService = require('~/services/token')
const Question = require('~/models/question')
const {
roles: { TUTOR }
} = require('~/consts/auth')
Expand Down Expand Up @@ -36,8 +37,13 @@ const studentUserData = {
lastLoginAs: 'student'
}

const updateData = {
title: 'Here is updated one!',
type: 'multipleChoice'
}

describe('Question controller', () => {
let app, server, accessToken, currentUser, studentAccessToken, testQuestion
let app, server, accessToken, currentUser, studentAccessToken, testQuestion, testQuestionId

beforeAll(async () => {
;({ app, server } = await serverInit())
Expand All @@ -50,6 +56,7 @@ describe('Question controller', () => {
currentUser = TokenService.validateAccessToken(accessToken)

testQuestion = await app.post(endpointUrl).send(testQuestionData).set('Authorization', `Bearer ${accessToken}`)
testQuestionId = testQuestion.body._id
})

afterEach(async () => {
Expand Down Expand Up @@ -110,4 +117,36 @@ describe('Question controller', () => {
expectError(403, FORBIDDEN, response)
})
})

describe(`PATCH ${endpointUrl}:id`, () => {
it('should update a question', async () => {
const response = await app
.patch(endpointUrl + testQuestionId)
.send(updateData)
.set('Authorization', `Bearer ${accessToken}`)
expect(response.statusCode).toBe(204)

const updatedQuestion = await Question.findById(testQuestionId)

expect(updatedQuestion).toMatchObject({
...testQuestionData,
...updateData
})
})

it('should throw UNAUTHORIZED', async () => {
const response = await app.patch(endpointUrl + testQuestionId).send(updateData)

expectError(401, UNAUTHORIZED, response)
})

it('should throw FORBIDDEN', async () => {
const response = await app
.patch(endpointUrl + testQuestionId)
.send(updateData)
.set('Authorization', `Bearer ${studentAccessToken}`)

expectError(403, FORBIDDEN, response)
})
})
})