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

Added create questions enpoint #569

Merged
merged 6 commits into from
Sep 15, 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
3 changes: 2 additions & 1 deletion consts/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const enums = {
COOPERATION_STATUS_ENUM: ['pending', 'active', 'declined', 'closed'],
PARAMS_ENUM: ['id', 'categoryId', 'subjectId'],
OFFER_STATUS_ENUM: ['active', 'draft', 'closed'],
NOTIFICATION_TYPE_ENUM: ['new', 'requested', 'active', 'declined', 'updated', 'closed', 'deleted']
NOTIFICATION_TYPE_ENUM: ['new', 'requested', 'active', 'declined', 'updated', 'closed', 'deleted'],
QUESTION_TYPE_ENUM: ['multipleChoice', 'openAnswer', 'oneAnswer']
}

module.exports = {
Expand Down
12 changes: 11 additions & 1 deletion controllers/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ const getQuestions = async (req, res) => {
res.status(200).json(questions)
}

const createQuestion = async (req, res) => {
const { id: author } = req.user
const data = req.body

const newQuestion = await questionService.createQuestion(author, data)

res.status(201).json(newQuestion)
}

module.exports = {
getQuestions
getQuestions,
createQuestion
}
64 changes: 64 additions & 0 deletions docs/questions/questions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
paths:
/questions:
post:
security:
- bearerAuth: []
tags:
- Questions
summary: Creates a new question.
description: Creates a new question.
produces:
- application/json
requestBody:
required: true
content:
application/json:
schema:
$ref: '#definitions/questionBody'
example:
title: What is the chemical symbol for water?
answers:
- text: First answer
isCorrect: true
- text: Second answer
isCorrect: false
type: multipleChoice
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#definitions/question'
example:
_id: 63ec1cd51e9d781cdb6f4b14
title: What is the chemical symbol for water?
answers:
- text: First answer
isCorrect: true
- text: Second answer
isCorrect: false
type: multipleChoice
author: 63da8767c9ad4c9a0b0eacd3
createdAt: 2023-02-14T23:44:21.334Z
updatedAt: 2023-02-14T23:44:21.334Z
'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.
19 changes: 17 additions & 2 deletions models/question.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
const { Schema, model } = require('mongoose')

const {
enums: { QUESTION_TYPE_ENUM }
} = require('~/consts/validation')
const { QUESTION, USER } = require('~/consts/models')
const { FIELD_CANNOT_BE_EMPTY, FIELD_CANNOT_BE_LONGER, FIELD_CANNOT_BE_SHORTER } = require('~/consts/errors')
const {
FIELD_CANNOT_BE_EMPTY,
FIELD_CANNOT_BE_LONGER,
FIELD_CANNOT_BE_SHORTER,
ENUM_CAN_BE_ONE_OF
} = require('~/consts/errors')

const questionSchema = new Schema(
{
Expand All @@ -27,6 +34,14 @@ const questionSchema = new Schema(
}
}
],
type: {
type: String,
enum: {
values: QUESTION_TYPE_ENUM,
message: ENUM_CAN_BE_ONE_OF('type', QUESTION_TYPE_ENUM)
},
required: true
},
author: {
type: Schema.Types.ObjectId,
ref: USER,
Expand Down
7 changes: 6 additions & 1 deletion routes/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ const router = require('express').Router()

const questionController = require('~/controllers/question')
const asyncWrapper = require('~/middlewares/asyncWrapper')
const { authMiddleware } = require('~/middlewares/auth')
const { authMiddleware, restrictTo } = require('~/middlewares/auth')
const {
roles: { TUTOR }
} = require('~/consts/auth')

router.use(authMiddleware)

router.get('/', asyncWrapper(questionController.getQuestions))
router.use(restrictTo(TUTOR))
router.post('/', asyncWrapper(questionController.createQuestion))

module.exports = router
12 changes: 11 additions & 1 deletion services/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@ const questionService = {
const count = await Question.countDocuments(match)

return { items, count }
},

createQuestion: async (author, data) => {
const { title, answers, type } = data

return await Question.create({
title,
answers,
type,
author
})
}
}

module.exports = questionService
62 changes: 52 additions & 10 deletions test/integration/controllers/question.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
const { serverInit, serverCleanup, stopServer } = require('~/test/setup')
const { expectError } = require('~/test/helpers')
const { UNAUTHORIZED } = require('~/consts/errors')
const { UNAUTHORIZED, FORBIDDEN } = require('~/consts/errors')
const testUserAuthentication = require('~/utils/testUserAuth')
const TokenService = require('~/services/token')
const {
roles: { TUTOR }
} = require('~/consts/auth')
const Question = require('~/models/question')

const endpointUrl = '/questions/'

Expand All @@ -21,23 +20,36 @@ const testQuestionData = {
text: 'Yes, of course',
isCorrect: false
}
]
],
type: 'multipleChoice'
}

const studentUserData = {
role: 'student',
firstName: 'Yamada',
lastName: 'Kizen',
email: '[email protected]',
password: 'ninpopass',
appLanguage: 'en',
isEmailConfirmed: true,
lastLogin: new Date().toJSON(),
lastLoginAs: 'student'
}

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

beforeAll(async () => {
;({ app, server } = await serverInit())
})

beforeEach(async () => {
accessToken = await testUserAuthentication(app, { role: TUTOR })
studentAccessToken = await testUserAuthentication(app, studentUserData)

currentUser = TokenService.validateAccessToken(accessToken)

testQuestion = await Question.create({ author: currentUser.id, ...testQuestionData })
testQuestion = await app.post(endpointUrl).send(testQuestionData).set('Authorization', `Bearer ${accessToken}`)
})

afterEach(async () => {
Expand All @@ -50,22 +62,52 @@ describe('Question controller', () => {

describe(`GET ${endpointUrl}`, () => {
it('should return list of questions', async () => {
const questions = await app.get(endpointUrl).set('Authorization', accessToken)
const questions = await app.get(endpointUrl).set('Authorization', `Bearer ${accessToken}`)

expect(questions.statusCode).toBe(200)
expect(questions.count).toBe(1)
expect(questions.items).toContainObject({
_id: testQuestion._id,
expect(questions.body.count).toBe(1)
expect(questions.body.items).toMatchObject([
{
_id: testQuestion.body._id,
createdAt: expect.any(String),
updatedAt: expect.any(String),
...testQuestionData
}
])
})

it('should throw UNAUTHORIZED', async () => {
const response = await app.get(endpointUrl)

expectError(401, UNAUTHORIZED, response)
})
})

describe(`POST ${endpointUrl}`, () => {
it('should create a new question', async () => {
expect(testQuestion.statusCode).toBe(201)
expect(testQuestion._body).toMatchObject({
_id: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String),
author: currentUser.id,
...testQuestionData
})
})

it('should throw UNAUTHORIZED', async () => {
const response = await app.get(endpointUrl)
const response = await app.post(endpointUrl)

expectError(401, UNAUTHORIZED, response)
})

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

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