From 9182f64e461be99e7bbc3a62b11dcc7cd9e4cd1f Mon Sep 17 00:00:00 2001 From: TresorRw Date: Sat, 4 May 2024 02:06:35 +0200 Subject: [PATCH] Documentation --- .husky/pre-commit | 2 +- package.json | 2 +- ...js => 20240501212252-rating-categories.js} | 0 ...Field.js => 20240501213041-RatingField.js} | 0 ...s.js => 20240503210253-overall-reviews.js} | 4 - ...ews.js => 20240503210456-field-reviews.js} | 7 ++ src/database/models/fieldReview.js | 7 ++ src/database/models/overallReview.js | 4 - src/database/relationships/index.js | 4 + src/documentation/index.js | 4 + src/documentation/ratingCategory/index.js | 106 ++++++++++++++++++ src/documentation/ratingField/index.js | 79 +++++++++++++ src/documentation/review/index.js | 15 ++- .../controllers/ratingCategoryController.js | 47 +++++--- .../controllers/ratingFieldController.js | 18 +-- src/restful/controllers/reviewControllers.js | 92 ++++++++++----- .../middlewares/validateRequestBody.js | 25 +++++ src/restful/routes/ratingCategoryRoutes.js | 11 ++ src/restful/routes/ratingFieldRoutes.js | 10 +- src/restful/routes/reviewRouter.js | 3 + src/services/ratingCategoryService.js | 14 +++ src/services/reviewServices.js | 63 ++++++++--- src/system/validators/rating.js | 16 +++ 23 files changed, 447 insertions(+), 86 deletions(-) rename src/database/migrations/{20240501213041-rating-categories.js => 20240501212252-rating-categories.js} (100%) rename src/database/migrations/{20240501212252-RatingField.js => 20240501213041-RatingField.js} (100%) rename src/database/migrations/{20240501214146-overall-reviews.js => 20240503210253-overall-reviews.js} (92%) rename src/database/migrations/{20240501213732-field-reviews.js => 20240503210456-field-reviews.js} (83%) create mode 100644 src/documentation/ratingCategory/index.js create mode 100644 src/documentation/ratingField/index.js create mode 100644 src/restful/middlewares/validateRequestBody.js diff --git a/.husky/pre-commit b/.husky/pre-commit index e0def98..5216956 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,4 +2,4 @@ . "$(dirname -- "$0")/_/husky.sh" npm run lint:fix -# npm run test +npm run test diff --git a/package.json b/package.json index 1cec0ee..53cb6b7 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lint:fix": "eslint . --fix", "build": "babel src --out-dir dist --source-maps inline --copy-files", "start": "babel-node dist/app.js", - "test": "cross-env NODE_ENV=test npm run db:migrate && npm run db:reset && cross-env NODE_ENV=test PORT=4300 mocha --require @babel/register --exit 'src/tests/**/*.test.js'", + "test": "cross-env NODE_ENV=test npm run db:migrate && cross-env NODE_ENV=test npm run db:reset && cross-env NODE_ENV=test PORT=4300 mocha --require @babel/register --exit 'src/tests/**/*.test.js'", "test:coverage": " nyc mocha --coverage --recursive --require @babel/polyfill --require @babel/register */tests/*.js --timeout 500000000 --exit", "coverage": "nyc report --reporter=text-lcov | coveralls", "db:migrate": "sequelize db:migrate", diff --git a/src/database/migrations/20240501213041-rating-categories.js b/src/database/migrations/20240501212252-rating-categories.js similarity index 100% rename from src/database/migrations/20240501213041-rating-categories.js rename to src/database/migrations/20240501212252-rating-categories.js diff --git a/src/database/migrations/20240501212252-RatingField.js b/src/database/migrations/20240501213041-RatingField.js similarity index 100% rename from src/database/migrations/20240501212252-RatingField.js rename to src/database/migrations/20240501213041-RatingField.js diff --git a/src/database/migrations/20240501214146-overall-reviews.js b/src/database/migrations/20240503210253-overall-reviews.js similarity index 92% rename from src/database/migrations/20240501214146-overall-reviews.js rename to src/database/migrations/20240503210253-overall-reviews.js index 5b8b8bb..824b0f3 100644 --- a/src/database/migrations/20240501214146-overall-reviews.js +++ b/src/database/migrations/20240503210253-overall-reviews.js @@ -34,10 +34,6 @@ module.exports = { key: "id", }, }, - isReviewd: { - type: Sequelize.BOOLEAN, - defaultValue: false, - }, type: { type: Sequelize.STRING, }, diff --git a/src/database/migrations/20240501213732-field-reviews.js b/src/database/migrations/20240503210456-field-reviews.js similarity index 83% rename from src/database/migrations/20240501213732-field-reviews.js rename to src/database/migrations/20240503210456-field-reviews.js index e7d2cf9..f77d515 100644 --- a/src/database/migrations/20240501213732-field-reviews.js +++ b/src/database/migrations/20240503210456-field-reviews.js @@ -17,6 +17,13 @@ module.exports = { key: "id", }, }, + overallReviewId: { + type: Sequelize.UUID, + references: { + model: "tbl_overall_reviews", + key: "id", + }, + }, ratings: { type: Sequelize.INTEGER, allowNull: false, diff --git a/src/database/models/fieldReview.js b/src/database/models/fieldReview.js index 1df2e02..913ccf9 100644 --- a/src/database/models/fieldReview.js +++ b/src/database/models/fieldReview.js @@ -20,6 +20,13 @@ const FieldReviewModel = () => { key: "id", }, }, + overallReviewId: { + type: DataTypes.UUID, + references: { + model: "tbl_overall_reviews", + key: "id", + }, + }, ratings: { type: DataTypes.INTEGER, allowNull: false, diff --git a/src/database/models/overallReview.js b/src/database/models/overallReview.js index 0e79f09..d80c00c 100644 --- a/src/database/models/overallReview.js +++ b/src/database/models/overallReview.js @@ -38,10 +38,6 @@ const OverallReviewModel = () => { key: "id", }, }, - isReviewd: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, type: { type: DataTypes.STRING, }, diff --git a/src/database/relationships/index.js b/src/database/relationships/index.js index 4edc512..0afad71 100644 --- a/src/database/relationships/index.js +++ b/src/database/relationships/index.js @@ -82,13 +82,16 @@ export const associate = () => { DB.RatingCategory.hasMany(DB.RatingField, { foreignKey: "categoryId", + as: "ratingFields", }); DB.RatingField.belongsTo(DB.RatingCategory, { foreignKey: "categoryId", + as: "ratingCategory", }); DB.OverallReview.hasMany(DB.FieldReview, { + as: "fieldReviews", foreignKey: "overallReviewId", }); @@ -98,6 +101,7 @@ export const associate = () => { DB.FieldReview.belongsTo(DB.RatingField, { foreignKey: "ratingFieldId", + as: "ratingField", onDelete: "CASCADE", }); }; diff --git a/src/documentation/index.js b/src/documentation/index.js index 5f6d141..b7da801 100644 --- a/src/documentation/index.js +++ b/src/documentation/index.js @@ -7,6 +7,8 @@ import reviews from "./review"; import reviewCycles from "./reviewCycle"; import search from "./search"; import dashboard from "./auth/dashboard"; +import ratingCategories from "./ratingCategory"; +import ratingFields from "./ratingField"; const defaults = swaggerDoc.paths; @@ -26,6 +28,8 @@ const paths = { ...reviewCycles, ...reviews, ...search, + ...ratingCategories, + ...ratingFields, }; const config = { diff --git a/src/documentation/ratingCategory/index.js b/src/documentation/ratingCategory/index.js new file mode 100644 index 0000000..0b0d22f --- /dev/null +++ b/src/documentation/ratingCategory/index.js @@ -0,0 +1,106 @@ +import responses from "../responses"; + +const ratingCategories = { + "/ratingCategories": { + get: { + tags: ["RatingCategories"], + summary: "Get all rating categories", + description: + "Get all categories. Only admin users can get a list of all categories.", + security: [{ JWT: [] }], + parameters: [], + consumes: ["application/json"], + responses, + }, + post: { + tags: ["RatingCategories"], + summary: "Create a new rating category", + description: + "Create a new project. Only admin users can create categories.", + security: [{ JWT: [] }], + parameters: [ + { + in: "body", + name: "body", + required: true, + schema: { + example: { + name: "Rating category name", + }, + }, + }, + ], + consumes: ["application/json"], + responses, + }, + }, + + "/ratingCategories/{id}": { + get: { + tags: ["RatingCategories"], + summary: "Get a rating category", + description: "Get one rating category with its rating fields.", + security: [{ JWT: [] }], + parameters: [ + { + in: "path", + name: "id", + required: true, + schema: { + example: "5e5bb69e-26fe-4e61-b4f3-e9a9332066da", + }, + }, + ], + consumes: ["application/json"], + responses, + }, + patch: { + tags: ["RatingCategories"], + summary: "Change category name", + description: "Change rating category name", + security: [{ JWT: [] }], + parameters: [ + { + in: "path", + name: "id", + required: true, + schema: { + example: "566febd2-d3f3-4ae3-ac76-a5ef5426366d", + }, + }, + { + in: "body", + name: "body", + required: true, + schema: { + example: { + name: "New Name", + }, + }, + }, + ], + consumes: ["application/json"], + responses, + }, + delete: { + tags: ["RatingCategories"], + summary: "Delete a rating category", + description: "Delete a rating category. it will delete its rating fields", + security: [{ JWT: [] }], + parameters: [ + { + in: "path", + name: "id", + required: true, + schema: { + example: "8a2a4287-fd47-45f9-a1a0-42e24aeeeddz", + }, + }, + ], + consumes: ["application/json"], + responses, + }, + }, +}; + +export default ratingCategories; diff --git a/src/documentation/ratingField/index.js b/src/documentation/ratingField/index.js new file mode 100644 index 0000000..5f4b6b5 --- /dev/null +++ b/src/documentation/ratingField/index.js @@ -0,0 +1,79 @@ +import responses from "../responses"; + +const ratingFields = { + "/ratingFields": { + get: { + tags: ["RatingFields"], + summary: "Get all rating fields", + description: + "Get all projects. Only admin users can get a list of all projects.", + security: [{ JWT: [] }], + parameters: [], + consumes: ["application/json"], + responses, + }, + post: { + tags: ["RatingFields"], + summary: "Create a new rating field", + description: + "Create a new project. Only admin users can create projects.", + security: [{ JWT: [] }], + parameters: [ + { + in: "body", + name: "body", + required: true, + schema: { + example: { + categoryId: "41dcfb3c-9c7f-4829-9752-f0e1694fd6ea", + name: "Rating field name", + }, + }, + }, + ], + consumes: ["application/json"], + responses, + }, + }, + + "/ratingFields/{id}": { + get: { + tags: ["RatingFields"], + summary: "Get a rating field", + description: "Get one rating field with its rating fields.", + security: [{ JWT: [] }], + parameters: [ + { + in: "path", + name: "id", + required: true, + schema: { + example: "5e5bb69e-26fe-4e61-b4f3-e9a9332066da", + }, + }, + ], + consumes: ["application/json"], + responses, + }, + delete: { + tags: ["RatingFields"], + summary: "Delete a rating field", + description: "Delete a rating field. it will delete its rating fields", + security: [{ JWT: [] }], + parameters: [ + { + in: "path", + name: "id", + required: true, + schema: { + example: "8a2a4287-fd47-45f9-a1a0-42e24aeeeddz", + }, + }, + ], + consumes: ["application/json"], + responses, + }, + }, +}; + +export default ratingFields; diff --git a/src/documentation/review/index.js b/src/documentation/review/index.js index a65cc3a..ead3a6d 100644 --- a/src/documentation/review/index.js +++ b/src/documentation/review/index.js @@ -5,7 +5,7 @@ const reviews = { post: { tags: ["Reviews"], summary: "Create new reviews", - description: "Create new reviews.", + description: "Create new review. use the schema below", security: [{ JWT: [] }], parameters: [ { @@ -14,10 +14,19 @@ const reviews = { required: true, schema: { example: { + comment: "review description", revieweeId: "5e5bb69e-26fe-4e61-b4f3-e9a9332066da", reviewCycleId: "5e5bb69e-26fe-4e61-b4f3-e9a9332066da", - description: "review description", - ratings: 2, + fieldReviews: [ + { + ratingFieldId: "5e5bb69e-26fe-4e61-b4f3-e9a9332066da", + ratings: 2, + }, + { + ratingFieldId: "5e5bb69e-26fe-4e61-b4f3-e9a9332066da", + ratings: 5, + }, + ], }, }, }, diff --git a/src/restful/controllers/ratingCategoryController.js b/src/restful/controllers/ratingCategoryController.js index 7a8d977..003abaa 100644 --- a/src/restful/controllers/ratingCategoryController.js +++ b/src/restful/controllers/ratingCategoryController.js @@ -1,37 +1,27 @@ import Response from "../../system/helpers/Response"; import RatingCategoryService from "../../services/ratingCategoryService"; -import * as v from "valibot"; -import { ratingCategorySchema } from "../../system/validators"; -import { filterValidationError } from "../../system/utils"; const { createRatingCategory, findAllRatingCategories, findRatingCategoryById, checkRatingCategoryExists, + updateRatingCategoryName, } = RatingCategoryService; export default class RatingCategoryController { static async createRatingCategory(req, res) { try { - const results = v.safeParse(ratingCategorySchema, req.body); - if (!results.success) { - return Response.error(res, 400, { - message: "Please correct your inputs", - errors: filterValidationError(results.issues), - }); - } + const { name } = req.body; // Check existance - const ratingCategory = await checkRatingCategoryExists( - results.output.name - ); + const ratingCategory = await checkRatingCategoryExists(name); if (ratingCategory) { return Response.error(res, 409, { message: "Rating Category already exists", }); } - const createCategory = await createRatingCategory(results.output); - return Response.success(res, 200, { + const createCategory = await createRatingCategory({ name }); + return Response.success(res, 201, { message: "Rating Category created successfully", data: createCategory, }); @@ -87,4 +77,31 @@ export default class RatingCategoryController { return Response.error(res, 500, error); } } + static async updateRatingCategory(req, res) { + try { + const { id } = req.params; + const { name } = req.body; + + const ratingCategory = await findRatingCategoryById(id); + if (!ratingCategory) { + return Response.error(res, 404, { + message: "Rating Category not found", + }); + } + const exitingCategory = await checkRatingCategoryExists(name); + if (exitingCategory && exitingCategory.id != id) { + return Response.error(res, 409, { + message: "Rating category name already exists", + }); + } else { + const updatedCategory = await updateRatingCategoryName(id, name); + return Response.success(res, 200, { + message: "Rating category is updated successfully", + data: updatedCategory, + }); + } + } catch (error) { + return Response.error(res, 500, error); + } + } } diff --git a/src/restful/controllers/ratingFieldController.js b/src/restful/controllers/ratingFieldController.js index da94e0f..abbbe90 100644 --- a/src/restful/controllers/ratingFieldController.js +++ b/src/restful/controllers/ratingFieldController.js @@ -1,8 +1,5 @@ import Response from "../../system/helpers/Response"; import ratingFieldService from "../../services/ratingFieldService"; -import * as v from "valibot"; -import { ratingFieldSchema } from "../../system/validators"; -import { filterValidationError } from "../../system/utils"; const { createRatingField, @@ -14,24 +11,15 @@ const { export default class RatingFieldController { static async createRatingField(req, res) { try { - const results = v.safeParse(ratingFieldSchema, req.body); - if (!results.success) { - return Response.error(res, 400, { - message: "Please correct your inputs", - errors: filterValidationError(results.issues), - }); - } + const { name, categoryId } = req.body; // Check existance - const ratingFields = await checkRatingFieldExists( - results.output.name, - results.output.categoryId - ); + const ratingFields = await checkRatingFieldExists(name, categoryId); if (ratingFields) { return Response.error(res, 409, { message: "Rating Field already exists in given category", }); } - const createCategory = await createRatingField(results.output); + const createCategory = await createRatingField({ name, categoryId }); return Response.success(res, 200, { message: "Rating Field created successfully", data: createCategory, diff --git a/src/restful/controllers/reviewControllers.js b/src/restful/controllers/reviewControllers.js index a6aa4f2..5aa87ec 100644 --- a/src/restful/controllers/reviewControllers.js +++ b/src/restful/controllers/reviewControllers.js @@ -1,16 +1,62 @@ /* eslint-disable no-constant-condition */ -/* eslint-disable no-useless-catch */ import Response from "../../system/helpers/Response"; import { reviewerType } from "../../system/utils/riviewerType"; import ReviewService from "../../services/reviewServices"; import UserService from "../../services/userService"; import ReviewCycleService from "../../services/reviewCycleServices"; +import RatingFieldService from "../../services/ratingFieldService"; export default class ReviewControllers { static async create(req, res) { + const { id: reviewerId } = req.user; + + const { + revieweeId, + reviewCycleId, + fieldReviews: fieldReviewsData, + } = req.body; + + // Checking if ratingFieldId is not in the array of field reviews more than once + const duplicatesExist = fieldReviewsData.filter((currentReview, index) => + fieldReviewsData.some( + (otherReview, i) => + otherReview.ratingFieldId === currentReview.ratingFieldId && + i !== index + ) + ); + if (duplicatesExist.length > 0) { + return Response.error(res, 400, { + message: "You have duplicate ratingFieldIds in your field reviews", + }); + } + try { - const { id: reviewerId } = req.user; - const { revieweeId, description, ratings, reviewCycleId } = req.body; + const allRatingFields = await RatingFieldService.findAllRatingFields(); + + // if number of rating fields does not match with user provided rating fields + if (allRatingFields.length !== fieldReviewsData.length) { + return Response.error(res, 400, { + message: "Please provide all ratingFieldIds required for a review", + }); + } + + // check if user provided ratingFields that are not in the database + const misMatches = fieldReviewsData + .filter( + (fieldReview) => + !allRatingFields.some( + (ratingField) => ratingField.id === fieldReview.ratingFieldId + ) + ) + .map((fieldReview) => fieldReview.ratingFieldId); + + if (misMatches.length > 0) { + return Response.error(res, 400, { + message: `We ca't find these ratingFieldIds: ${misMatches.join( + ", " + )}`, + }); + } const selectedReviewer = await ReviewService.findReviewer( reviewerId, @@ -66,34 +112,28 @@ export default class ReviewControllers { type, }); if (reviewMade) { - return Response.error(res, 401, { - message: "review have been made", + return Response.error(res, 409, { + message: "Review have been made", }); } - let ratingz = type === "manager review" ? Number(ratings) : null; - ReviewService.create({ - revieweeId, - reviewerId, - description, + + const overallReview = await ReviewService.createReview({ + ...req.body, type, - ratingz, - reviewCycleId, - }) - .then((review) => { - return Response.success(res, 200, { - message: "review created successfully", - data: review, - }); - }) - .catch((error) => { - return Response.error(res, 403, { - message: "review failed to be created", - error: error.message, - }); - }); + reviewerId, + }); + const { id } = overallReview; + const fieldReviews = await ReviewService.createFieldReviews( + fieldReviewsData, + id + ); + return Response.success(res, 201, { + message: "Review created successfully", + data: { ...overallReview.dataValues, fieldReviews }, + }); } catch (error) { return Response.error(res, 500, { - message: "server error", + message: "Problem while creating review", error: error.message, }); } diff --git a/src/restful/middlewares/validateRequestBody.js b/src/restful/middlewares/validateRequestBody.js new file mode 100644 index 0000000..aa0814f --- /dev/null +++ b/src/restful/middlewares/validateRequestBody.js @@ -0,0 +1,25 @@ +import * as v from "valibot"; +import { filterValidationError } from "../../system/utils"; +import Response from "../../system/helpers/Response"; + +export function validateRequestBody(validationSchema) { + return (req, res, next) => { + const results = v.safeParse(validationSchema, req.body); + if (!results.success) { + return Response.error(res, 400, { + message: "Please correct your inputs", + errors: filterValidationError(results.issues), + }); + } + if ( + results.output.fieldReviews && + results.output.fieldReviews.length === 0 + ) { + return Response.error(res, 400, { + message: "Please provide at least one field review", + }); + } + req.body = results.output; + return next(); + }; +} diff --git a/src/restful/routes/ratingCategoryRoutes.js b/src/restful/routes/ratingCategoryRoutes.js index 7dd37ce..8e4a9c0 100644 --- a/src/restful/routes/ratingCategoryRoutes.js +++ b/src/restful/routes/ratingCategoryRoutes.js @@ -2,12 +2,15 @@ import { Router } from "express"; import allowedRole from "../middlewares/allowedRoles"; import RatingCategoryController from "../controllers/ratingCategoryController"; import protect from "../middlewares"; +import { validateRequestBody } from "../middlewares/validateRequestBody"; +import { ratingCategorySchema } from "../../system/validators"; const { createRatingCategory, findAllRatingCategories, findRatingCategoryById, deleteRatingCategory, + updateRatingCategory, } = RatingCategoryController; const ratingCategoryRouter = Router(); @@ -16,6 +19,7 @@ ratingCategoryRouter.post( "/", protect, allowedRole(["admin"]), + validateRequestBody(ratingCategorySchema), createRatingCategory ); ratingCategoryRouter.get( @@ -30,6 +34,13 @@ ratingCategoryRouter.get( allowedRole(["admin"]), findRatingCategoryById ); +ratingCategoryRouter.patch( + "/:id", + protect, + allowedRole(["admin"]), + validateRequestBody(ratingCategorySchema), + updateRatingCategory +); ratingCategoryRouter.delete( "/:id", protect, diff --git a/src/restful/routes/ratingFieldRoutes.js b/src/restful/routes/ratingFieldRoutes.js index 302f48a..21594d9 100644 --- a/src/restful/routes/ratingFieldRoutes.js +++ b/src/restful/routes/ratingFieldRoutes.js @@ -2,6 +2,8 @@ import { Router } from "express"; import allowedRole from "../middlewares/allowedRoles"; import ratingFieldController from "../controllers/ratingFieldController"; import protect from "../middlewares"; +import { validateRequestBody } from "../middlewares/validateRequestBody"; +import { ratingFieldSchema } from "../../system/validators"; const { createRatingField, @@ -12,7 +14,13 @@ const { const ratingFieldRouter = Router(); -ratingFieldRouter.post("/", protect, allowedRole(["admin"]), createRatingField); +ratingFieldRouter.post( + "/", + protect, + allowedRole(["admin"]), + validateRequestBody(ratingFieldSchema), + createRatingField +); ratingFieldRouter.get( "/", protect, diff --git a/src/restful/routes/reviewRouter.js b/src/restful/routes/reviewRouter.js index f1c233d..b704b7c 100644 --- a/src/restful/routes/reviewRouter.js +++ b/src/restful/routes/reviewRouter.js @@ -2,6 +2,8 @@ import express from "express"; import ReviewControllers from "../controllers/reviewControllers"; import protect from "../middlewares"; import allowedRole from "../middlewares/allowedRoles"; +import { validateRequestBody } from "../middlewares/validateRequestBody"; +import { overallReviewSchema } from "../../system/validators"; const router = express.Router(); @@ -9,6 +11,7 @@ router.post( "/", protect, allowedRole(["developer", "architect"]), + validateRequestBody(overallReviewSchema), ReviewControllers.create ); router.get("/", protect, ReviewControllers.getAll); diff --git a/src/services/ratingCategoryService.js b/src/services/ratingCategoryService.js index 47dd54d..2b291de 100644 --- a/src/services/ratingCategoryService.js +++ b/src/services/ratingCategoryService.js @@ -19,6 +19,7 @@ export default class RatingCategoryService { include: [ { model: RatingField, + as: "ratingFields", attributes: ["id", "name"], }, ], @@ -50,6 +51,7 @@ export default class RatingCategoryService { include: [ { model: RatingField, + as: "ratingFields", attributes: ["id", "name"], }, ], @@ -59,4 +61,16 @@ export default class RatingCategoryService { throw error; } } + + static async updateRatingCategoryName(id, name) { + try { + const updatedCategory = await RatingCategory.update( + { name }, + { where: { id }, returning: true } + ); + return updatedCategory[1][0]; + } catch (error) { + throw error; + } + } } diff --git a/src/services/reviewServices.js b/src/services/reviewServices.js index 1e2b891..147ec6d 100644 --- a/src/services/reviewServices.js +++ b/src/services/reviewServices.js @@ -1,29 +1,48 @@ /* eslint-disable no-useless-catch */ import { Op } from "sequelize"; import db from "./../database"; -const { Review, Reviewer, User } = db; +const { OverallReview, Reviewer, User, FieldReview, RatingField } = db; export default class ReviewService { - /** - * Creates a new message. - * @param {object} param details of a message. - * @returns {object} users new message. - */ - - static async create(param) { - const review = await Review.create(param); - return review; + static async createReview(params) { + const { comment, reviewerId, revieweeId, reviewCycleId, type } = params; + try { + const overallReview = await OverallReview.create({ + comment, + reviewerId, + revieweeId, + reviewCycleId, + type, + }); + return overallReview; + } catch (error) { + throw error; + } + } + + static async createFieldReviews(params, overallReviewId) { + try { + const fieldReviews = await FieldReview.bulkCreate( + params.map((fieldReview) => ({ + ...fieldReview, + overallReviewId, + })) + ); + return fieldReviews; + } catch (error) { + throw error; + } } static async findONE(param) { - const Reviews = await Review.findOne({ + const Reviews = await OverallReview.findOne({ where: param, }); return Reviews; } static async findAll() { - const Reviews = await Review.findAll({ + const Reviews = await OverallReview.findAll({ include: [ { model: User, @@ -35,18 +54,30 @@ export default class ReviewService { as: "reviewee", attributes: ["displayName", "email", "role"], }, + { + model: FieldReview, + as: "fieldReviews", + attributes: ["id", "ratings"], + include: [ + { + model: RatingField, + as: "ratingField", + attributes: ["name"], + }, + ], + }, ], }); return Reviews; } static async findById(id) { - const Review = await Review.findByPk(id); + const Review = await OverallReview.findByPk(id); return Review; } static async delete(id) { - const review = await Review.destroy(id); + const review = await OverallReview.destroy(id); return review; } @@ -171,7 +202,7 @@ export default class ReviewService { } static async getReviews(developerId, reviewCycleId) { - const givenReviews = await Review.findAll({ + const givenReviews = await OverallReview.findAll({ where: { reviewCycleId, reviewerId: developerId, @@ -194,7 +225,7 @@ export default class ReviewService { ], }); - const receivedReviews = await Review.findAll({ + const receivedReviews = await OverallReview.findAll({ where: { reviewCycleId, revieweeId: developerId, diff --git a/src/system/validators/rating.js b/src/system/validators/rating.js index 3e857ec..122ef92 100644 --- a/src/system/validators/rating.js +++ b/src/system/validators/rating.js @@ -6,3 +6,19 @@ export const ratingFieldSchema = v.object({ }); export const ratingCategorySchema = v.pick(ratingFieldSchema, ["name"]); + +export const overallReviewSchema = v.object({ + comment: v.string([v.minLength(1, "Comment should not be empty.")]), + revieweeId: v.uuid("Invalid Reviewee ID."), + reviewCycleId: v.uuid("Invalid Review Cycle ID."), + fieldReviews: v.array( + v.object({ + ratingFieldId: v.uuid("Invalid Rating Field ID."), + ratings: v.number([ + v.minValue(1, "Rating should be greater than or equal to 1."), + v.maxValue(5, "Rating should be less than or equal to 5."), + ]), + }), + "Field reviews should be an array of objects." + ), +});