diff --git a/controllers/question.js b/controllers/question.js new file mode 100644 index 00000000..42e6d510 --- /dev/null +++ b/controllers/question.js @@ -0,0 +1,18 @@ +const questionService = require('~/services/question') +const getRegex = require('~/utils/getRegex') +const getSortOptions = require('~/utils/getSortOptions') + +const getQuestions = async (req, res) => { + const { title, sort, skip, limit } = req.query + + const match = { title: getRegex(title) } + const sortOptions = getSortOptions(sort) + + const questions = await questionService.getQuestions(match, sortOptions, parseInt(skip), parseInt(limit)) + + res.status(200).json(questions) +} + +module.exports = { + getQuestions +} diff --git a/docs/questions/questions.schema.yaml b/docs/questions/question.schema.yaml similarity index 100% rename from docs/questions/questions.schema.yaml rename to docs/questions/question.schema.yaml diff --git a/docs/questions/question.yaml b/docs/questions/question.yaml new file mode 100644 index 00000000..6930ca03 --- /dev/null +++ b/docs/questions/question.yaml @@ -0,0 +1,74 @@ +paths: + /questions: + get: + security: + - bearerAuth: [] + tags: + - Questions + summary: Find all questions. + description: Finds and returns an array with a list of all questions. + produces: + - application/json + parameters: + - in: query + name: title + schema: + type: string + required: false + - in: query + name: sort + schema: + type: string + required: false + - in: query + name: skip + schema: + type: string + required: false + - in: query + name: limit + schema: + type: string + required: false + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/definitions/quizBody' + example: + items: + - _id: 64ca5914b57f2442403394a5 + title: Is Assembly best programming language? + answers: + - text: Yes, + isCorrect: false + - text: Yes, of course, + isCorrect: true + author: '6477007a6fa4d05e1a800ce5' + createdAt: 2023-20-01T13:25:36.292Z + updatedAt: 2023-20-01T13:25:36.292Z + - _id: 64ca5932b57f2442403394a9 + title: What is the chemical symbol for water? + answers: + - text: H2O, + isCorrect: false + - text: O2H + isCorrect: true + - text: H2O2 + isCorrect: true + createdAt: 2023-20-01T13:25:36.292Z + updatedAt: 2023-20-01T13:25:36.292Z + author: '6477007a6fa4d05e1a800ce5' + count: 2 + 401: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/Error' + example: + status: 401 + code: UNAUTHORIZED + message: The requested URL requires user authorization. \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index d58d7653..97b25ace 100644 --- a/routes/index.js +++ b/routes/index.js @@ -19,6 +19,7 @@ const course = require('~/routes/course') const quiz = require('~/routes/quiz') const attachment = require('~/routes/attachment') const finishedQuiz = require('~/routes/finishedQuiz') +const question = require('~/routes/question') router.use('/auth', auth) router.use('/users', user) @@ -39,5 +40,6 @@ router.use('/courses', course) router.use('/quizzes', quiz) router.use('/attachments', attachment) router.use('/finished-quizzes', finishedQuiz) +router.use('/questions', question) module.exports = router diff --git a/routes/question.js b/routes/question.js new file mode 100644 index 00000000..9baec467 --- /dev/null +++ b/routes/question.js @@ -0,0 +1,11 @@ +const router = require('express').Router() + +const questionController = require('~/controllers/question') +const asyncWrapper = require('~/middlewares/asyncWrapper') +const { authMiddleware } = require('~/middlewares/auth') + +router.use(authMiddleware) + +router.get('/', asyncWrapper(questionController.getQuestions)) + +module.exports = router diff --git a/services/question.js b/services/question.js new file mode 100644 index 00000000..5d4de36c --- /dev/null +++ b/services/question.js @@ -0,0 +1,19 @@ +const Question = require('~/models/question') + +const questionService = { + getQuestions: async (match, sort, skip = 0, limit = 10) => { + const items = await Question + .find(match) + .collation({ locale: 'en', strength: 1 }) + .sort(sort) + .skip(skip) + .limit(limit) + .lean() + .exec() + const count = await Question.countDocuments(match) + + return { items, count } + } +} + +module.exports = questionService diff --git a/test/integration/controllers/question.spec.js b/test/integration/controllers/question.spec.js new file mode 100644 index 00000000..550f9c06 --- /dev/null +++ b/test/integration/controllers/question.spec.js @@ -0,0 +1,71 @@ +const { serverInit, serverCleanup, stopServer } = require('~/test/setup') +const { expectError } = require('~/test/helpers') +const { UNAUTHORIZED } = 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/' + +const testQuestionData = { + title: 'Assembly', + answers: [ + { + text: 'Yes', + isCorrect: true + }, + { + text: 'Yes, of course', + isCorrect: false + } + ] +} + + +describe('Question controller', () => { + let app, server, accessToken, currentUser, testQuestion + + beforeAll(async () => { + ;({ app, server } = await serverInit()) + }) + + beforeEach(async () => { + accessToken = await testUserAuthentication(app, { role: TUTOR }) + + currentUser = TokenService.validateAccessToken(accessToken) + + testQuestion = await Question.create({ author: currentUser.id, ...testQuestionData }) + }) + + afterEach(async () => { + await serverCleanup() + }) + + afterAll(async () => { + await stopServer(server) + }) + + describe(`GET ${endpointUrl}`, () => { + it('should return list of questions', async () => { + const questions = await app.get(endpointUrl).set('Authorization', accessToken) + + expect(questions.statusCode).toBe(200) + expect(questions.count).toBe(1) + expect(questions.items).toContainObject({ + _id: testQuestion._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) + }) + }) +})