diff --git a/src/common/utils/catch-async.js b/src/common/utils/catch-async.js new file mode 100644 index 0000000..d73c162 --- /dev/null +++ b/src/common/utils/catch-async.js @@ -0,0 +1,15 @@ +const sendResponse = require('../utils/response-handler'); + +exports.catchAsync = (fn, errorMessage) => { + return async (req, res) => { + try { + await fn(req, res); + } catch (error) { + console.error(error); + if (error?.type) { + return sendResponse.badRequest(res, error.message); + } + return sendResponse.fail(req, res, errorMessage); + } + }; +}; diff --git a/src/common/utils/request.validator.js b/src/common/utils/request.validator.js index 86e7d54..72fabf6 100644 --- a/src/common/utils/request.validator.js +++ b/src/common/utils/request.validator.js @@ -1,17 +1,20 @@ -const ErrorMessage = require('../../common/constants/error-message'); const Ajv = require('ajv'); const addFormats = require('ajv-formats'); +const ErrorMessage = require('../../common/constants/error-message'); + const ajv = new Ajv(); addFormats(ajv); exports.validateRequest = (schema, req) => { const validate = ajv.compile(schema); - const isValid = validate(req); - if (isValid) { + return validateAndHandleErrors(validate, req); +}; + +const validateAndHandleErrors = (validate, req) => { + if (validate(req)) { return req; - } else { - const { errors } = validate; - const message = errors && errors[0] ? errors[0].message : ErrorMessage.BAD_REQUEST; - throw { message, type: 'ajv' }; } + const { errors } = validate; + const message = errors && errors[0] ? errors[0].message : ErrorMessage.BAD_REQUEST; + throw { message, type: 'ajv' }; }; diff --git a/src/routes/word/word.controller.js b/src/routes/word/word.controller.js index 3180f2b..427303f 100644 --- a/src/routes/word/word.controller.js +++ b/src/routes/word/word.controller.js @@ -1,101 +1,73 @@ const wordService = require('./word.service'); const userService = require('../user/user.service'); +const { catchAsync } = require('../../common/utils/catch-async'); const sendResponse = require('../../common/utils/response-handler'); const ErrorMessage = require('../../common/constants/error-message'); const SuccessMessage = require('../../common/constants/success-message'); const { validateRequest } = require('../../common/utils/request.validator'); const { searchTermSchema, relatedTermSchema, wordListSchema } = require('./word.schema'); -exports.getSearchWords = async (req, res) => { - try { - const _id = req?.user ? req.user._id : null; - const validData = validateRequest(searchTermSchema, req.params); - let searchTerm = validData.searchTerm; - const data = await wordService.getSearchWords(searchTerm); - if (_id) { - await userService.updateRecentSearch(_id, searchTerm); - } - const message = data ? SuccessMessage.SEARCH_WORDS_SUCCESS : SuccessMessage.SEARCH_WORDS_NONE; - sendResponse.ok(res, { - message, - data, - }); - } catch (error) { - console.log(error); - if (error?.type) { - return sendResponse.badRequest(res, error.message); - } - sendResponse.fail(req, res, ErrorMessage.SEARCH_WORDS_ERROR); - } -}; +exports.getSearchWords = catchAsync(async (req, res) => { + const validData = validateRequest(searchTermSchema, req.params); + const { searchTerm } = validData; + const data = await wordService.getSearchWords(searchTerm); + await updateRecentWordIfLogined(req, searchTerm); -exports.getRankWords = async (req, res) => { - try { - const data = await wordService.getRankWords(); - sendResponse.ok(res, { - message: SuccessMessage.RANK_WORDS_SUCCESS, - data, - }); - } catch (error) { - sendResponse.fail(req, res, ErrorMessage.RANK_WORDS_ERROR); - } -}; + const message = data ? SuccessMessage.SEARCH_WORDS_SUCCESS : SuccessMessage.SEARCH_WORDS_NONE; + sendResponse.ok(res, { message, data }); +}, ErrorMessage.SEARCH_WORDS_ERROR); -exports.getRelatedWords = async (req, res) => { - try { - const { searchTerm, limit } = validateRequest(relatedTermSchema, req.query); - const data = await wordService.getRelatedWords(searchTerm, limit); - sendResponse.ok(res, { - message: SuccessMessage.RELATED_WORDS_SUCCESS, - data, - }); - } catch (error) { - console.log(error); - sendResponse.fail(req, res, ErrorMessage.RELATED_WORDS_ERROR); - } -}; +exports.getRankWords = catchAsync(async (req, res) => { + const data = await wordService.getRankWords(); + sendResponse.ok(res, { + message: SuccessMessage.RANK_WORDS_SUCCESS, + data, + }); +}, ErrorMessage.RANK_WORDS_ERROR); -exports.getAllWords = async (req, res) => { - try { - const { limit, page, sort } = validateRequest(wordListSchema, { - limit: req.query.limit * 1, - page: req.query.page * 1, - sort: req.query.sort, - }); +exports.getRelatedWords = catchAsync(async (req, res) => { + const { searchTerm, limit } = validateRequest(relatedTermSchema, req.query); + const data = await wordService.getRelatedWords(searchTerm, limit); + sendResponse.ok(res, { + message: SuccessMessage.RELATED_WORDS_SUCCESS, + data, + }); +}, ErrorMessage.RELATED_WORDS_ERROR); - const data = await wordService.getAllWords(sort, page, limit); +exports.getAllWords = catchAsync(async (req, res) => { + const { limit, page, sort } = validateRequest(wordListSchema, { + limit: req.query.limit * 1, + page: req.query.page * 1, + sort: req.query.sort, + }); - sendResponse.ok(res, { - message: SuccessMessage.GET_WORDS_SUCCESS, - data, - }); - } catch (error) { - console.log(error); - if (error?.type) { - return sendResponse.badRequest(res, error.message); - } - sendResponse.fail(req, res, ErrorMessage.GET_WORDS_ERROR); - } -}; - -exports.checkDuplicateWord = async (req, res) => { - try { - const { word } = req.body; - const isDataExist = await wordService.checkDuplicateWord(word); - data = { isDataExist }; + const data = await wordService.getAllWords(sort, page, limit); - if (isDataExist) { - return sendResponse.ok(res, { - message: ErrorMessage.EXIST_WORD, - data, - }); - } + sendResponse.ok(res, { + message: SuccessMessage.GET_WORDS_SUCCESS, + data, + }); +}, ErrorMessage.GET_WORDS_ERROR); +exports.checkDuplicateWord = catchAsync(async (req, res) => { + const { word } = req.body; + const isDataExist = await wordService.checkDuplicateWord(word); + if (isDataExist) { return sendResponse.ok(res, { - message: SuccessMessage.CHECK_DUPLICATE_REQUEST_SUCCESS, - data, + message: ErrorMessage.EXIST_WORD, + data: { isDataExist }, }); - } catch (error) { - sendResponse.fail(req, res, ErrorMessage.CHECK_DUPLICATE_REQUEST_ERROR); } -}; + + return sendResponse.ok(res, { + message: SuccessMessage.CHECK_DUPLICATE_REQUEST_SUCCESS, + data, + }); +}); + +async function updateRecentWordIfLogined(req, searchTerm) { + const userId = req.user?._id; + if (userId) { + await userService.updateRecentSearch(userId, searchTerm); + } +} diff --git a/src/routes/word/word.repository.js b/src/routes/word/word.repository.js index 617d7de..76b0c4e 100644 --- a/src/routes/word/word.repository.js +++ b/src/routes/word/word.repository.js @@ -2,161 +2,109 @@ const Word = require('./word.model'); const User = require('../user/user.model'); exports.getSearchWords = async (searchTerm) => { - try { - const escapedSearchTerm = searchTerm.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); - const searchWords = await Word.findOne({ word: { $regex: `^${escapedSearchTerm}$`, $options: 'i' } }); - - if (!searchWords) { - console.log('Search term not found in Word collection'); - } - return searchWords; - } catch (error) { - console.log('Error while getting search words:', error); - return null; - } + const escapedSearchTerm = searchTerm.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + return await Word.findOne({ word: { $regex: `^${escapedSearchTerm}$`, $options: 'i' } }); }; exports.getRankWords = async () => { - try { - const words = await Word.find().sort({ freq: -1 }).limit(10); - const wordNames = words.map((word) => word.word); - return wordNames; - } catch (error) { - console.log('Error while getting rank words:', error); - return null; - } + return await Word.find() + .sort({ freq: -1 }) + .limit(10) + .map((word) => word.word); }; exports.getRelatedWords = async (searchTerm, limit) => { - try { - const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const relatedWords = await Word.find({ word: new RegExp(escapedTerm, 'i') }) - .sort({ freq: -1 }) - .limit(parseInt(limit)); - const wordNames = relatedWords.map((word) => word.word); - return wordNames; - } catch (error) { - console.log('Error while getting related words:', error); - return null; - } + const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const relatedWords = await Word.find({ word: new RegExp(escapedTerm, 'i') }) + .sort({ freq: -1 }) + .limit(parseInt(limit)); + return relatedWords.map((word) => word.word); }; exports.getAllWords = async (isSorted, page, limit) => { - try { - const skip = (page - 1) * limit; - const sortOrder = {}; - const collation = { locale: 'en', strength: 2 }; - - if (isSorted === 'asc' || isSorted === 'desc') { - sortOrder.word = isSorted === 'asc' ? 1 : -1; - } else if (isSorted === 'popularity') { - sortOrder.freq = -1; - sortOrder.word = 1; - } else if (isSorted === 'recent') { - sortOrder.createdAt = -1; - sortOrder.word = 1; - } - - const words = await Word.find().collation(collation).sort(sortOrder).skip(skip).limit(parseInt(limit, 10)); - - return words; - } catch (error) { - console.log('Error while getting all words:', error); - return null; + const skip = (page - 1) * limit; + const sortOrder = {}; + const collation = { locale: 'en', strength: 2 }; + + if (isSorted === 'asc' || isSorted === 'desc') { + sortOrder.word = isSorted === 'asc' ? 1 : -1; + } else if (isSorted === 'popularity') { + sortOrder.freq = -1; + sortOrder.word = 1; + } else if (isSorted === 'recent') { + sortOrder.createdAt = -1; + sortOrder.word = 1; } + + return await Word.find().collation(collation).sort(sortOrder).skip(skip).limit(parseInt(limit, 10)); }; exports.addWord = async (requestId, formData) => { - try { - const user = await User.findOne({ 'requests._id': requestId }); - if (!user) { - console.log('User with the given request not found'); - return null; - } - - const request = user.requests.id(requestId); - if (!request) { - console.log('Request not found'); - return null; - } - - const newWord = new Word({ - word: formData.devTerm, - awkPron: formData.awkPron, - comPron: formData.commonPron, - info: formData.addInfo, - suggestedBy: request.suggestedBy, - }); - - await newWord.save(); - - console.log('Word added successfully'); - } catch (error) { - console.log('Error while adding word:', error); + const user = await User.findOne({ 'requests._id': requestId }); + if (!user) { + console.log('User with the given request not found'); + return null; + } + + const request = user.requests.id(requestId); + if (!request) { + console.log('Request not found'); return null; } + + const newWord = new Word({ + word: formData.devTerm, + awkPron: formData.awkPron, + comPron: formData.commonPron, + info: formData.addInfo, + suggestedBy: request.suggestedBy, + }); + + await newWord.save(); }; exports.updateWord = async (requestId, formData) => { - try { - const user = await User.findOne({ 'requests._id': requestId }); - if (!user) { - console.log('User with the given request not found'); - return null; - } - - const request = user.requests.id(requestId); - if (!request) { - console.log('Request not found'); - return null; - } - - const wordToUpdate = await Word.findOne({ word: request.word }); - if (!wordToUpdate) { - console.log('Word not found in Word collection'); - return null; - } - - wordToUpdate.word = formData.devTerm; - wordToUpdate.awkPron = formData.awkPron; - wordToUpdate.comPron = formData.commonPron; - wordToUpdate.info = formData.addInfo; - wordToUpdate.suggestedBy = request.suggestedBy; - - await wordToUpdate.save(); - - console.log('Word updated successfully'); - return wordToUpdate; - } catch (error) { - console.log('Error while updating word:', error); + const user = await User.findOne({ 'requests._id': requestId }); + if (!user) { + console.log('User with the given request not found'); return null; } + + const request = user.requests.id(requestId); + if (!request) { + console.log('Request not found'); + return null; + } + + const findWord = await Word.findOne({ word: request.word }); + if (!findWord) { + console.log('Word not found in Word collection'); + return null; + } + + updateWordFields(findWord, formData, request); + + await findWord.save(); + + return findWord; }; exports.deleteWordContributor = async (_id) => { - try { - const user = await User.findById(_id); - - if (!user) { - console.log('User not found'); - return null; - } - const nickname = user.nickname; - - const words = await Word.updateMany({ suggestedBy: nickname }, { suggestedBy: null }); - return words; - } catch (error) { - console.log('Error while deleting word contributor:', error); + const user = await User.findById(_id); + if (!user) { return null; } + return await Word.updateMany({ suggestedBy: user.nickname }, { suggestedBy: null }); }; exports.checkDuplicateWord = async (word) => { - try { - const escapedTerm = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const wordExists = await Word.findOne({ word: { $regex: new RegExp(`^${escapedTerm}$`, 'i') } }); - return wordExists; - } catch (error) { - console.log('Error while checking duplicate word:', error); - return null; - } + const escapedTerm = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + return await Word.findOne({ word: { $regex: new RegExp(`^${escapedTerm}$`, 'i') } }); +}; + +const updateWordFields = (wordToUpdate, formData, request) => { + wordToUpdate.word = formData.devTerm; + wordToUpdate.awkPron = formData.awkPron; + wordToUpdate.comPron = formData.commonPron; + wordToUpdate.info = formData.addInfo; + wordToUpdate.suggestedBy = request.suggestedBy; };