Skip to content

feat: + add fighters and fights management with validation, error han… #12

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions CustomError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class CustomError extends Error {
constructor(message, code) {
super();
this.message = message;
this.code = code;
}
}
44 changes: 44 additions & 0 deletions constants/response.messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
export const MESSAGES = {
USER_MESSAGES: {
ERROR_PASSWORD_LENGHT: "User password must be at least 3 characters long",
ERROR_EMAIL_FORMAT: "Only @gmail email is allowed",
ERROR_EMAIL_UNIQUE: "User email is in use",
ERROR_PHONE_FORMAT: "Only +380xxxxxxxxx phoneNumber format is allowed",
ERROR_PHONE_UNIQUE: "User phone is in use",
UNEXPECTED_ERROR_CREATING:
"Unexpected error creating user. Please, contact or try again",
ERROR_USER_CREDENTIAL_LOGIN: "Wrong user credentials",
ERROR_USER_VALIDATION_MIDDLEWARE:
"Missing body parameters. Required: firstName, lastName, email, phoneNumber, password",
ERROR_AUTH_VALIDATION_MIDDLEWARE:
"Missing body parameters. Required: email, password",
ERROR_USER_NOT_FOUND: "User id not found. No action performed.",
ERROR_USER_UPDATE_EMPTY_PARAMS:
"Required: firstName or lastName or email or phoneNumber or password.",
},
FIGHTER_MESSAGES: {
ERROR_NAME_UNIQUE: "Fighter name is already in use.",
UNEXPECTED_FIGHTER_CREATING:
"Unexpected error creating fighter. Please, contact or try again",
ERROR_FIGHTER_NOT_FOUND: "Fighter id not found. No action performed.",
ERROR_FIGHTER_CREATE_PARAMS:
"Missing body parameters. Required: name, power, defense",
ERROR_FIGHTER_UPDATE_EMPTY_PARAMS:
"Required: name or power or defense or health.",
ERROR_DEFENSE_RANGE_VALUE: "Fighter defense value must be between 1 to 10.",
ERROR_POWER_RANGE_VALUE: "Fighter power value must be between 1 to 100.",
ERROR_HEALTH_RANGE_VALUE: "Fighter health value must be between 80 to 120.",
},
FIGHT_MESSAGES: {
ERROR_FIGHT_UPDATE_PARAMS:
"Required: id url param and body params fighter1Shot, fighter2Shot, fighter1Health and fighter2Health",
ERROR_FIGHT_CREATE_PARAMS: "Required: fighter1 and fighter2.",
ERROR_FIGHTER_NOT_FOUND: "Fighter id not found.",
ERROR_FIGHT_NOT_FOUND: "Fight id not found.",
},
GENERIC_ERROR_MESSAGE: "Unexpected error. Please, contact or try again",
GENERIC_EMPTY_REQUEST_ERROR: "Requested data is not found.",
ERROR_SECURITY_TOKEN_OR_ID_MISSING: "Token or User id missing",
ERROR_SECURITY_TOKEN_BAD_CREDENTIAL: "Invalid token",
ERROR_NUMBER_TYPE_DATA_CAST: "Data type error. Can't cast to number:",
};
28 changes: 28 additions & 0 deletions helpers/middlewares.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export function checkEveryParamExistence(...params) {
return params.every(
(param) => param !== null && param !== undefined && param !== ""
);
}

export function checkAtLeastOneParamExist(...params) {
return params.some((param) => param !== null && param !== undefined);
}

export function filterOnlyExistingParams(ojbParams, currentObj) {
let copyObject = {};
for (let prop in ojbParams) {
if (
ojbParams[prop] !== null &&
ojbParams[prop] !== undefined &&
ojbParams[prop] !== ""
) {
copyObject[prop] = ojbParams[prop];
}
}

return { ...currentObj, ...copyObject };
}

export function emailToLowerCased(email) {
return email.toLowerCase();
}
13 changes: 13 additions & 0 deletions helpers/services.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MESSAGES } from "../constants/response.messages.js";
import { CustomError } from "../CustomError.js";

export function castValuesToNumber(value) {
const castedValue = Number(value);
if (isNaN(castedValue)) {
throw new CustomError(
`${MESSAGES.ERROR_NUMBER_TYPE_DATA_CAST} ${value}`,
400
);
}
return castedValue;
}
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ initRoutes(app);
app.use("/", express.static("./client/build"));

const port = 3050;
app.listen(port, () => {});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});

export { app };
39 changes: 39 additions & 0 deletions middlewares/fight.validation.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MESSAGES } from "../constants/response.messages.js";
import { CustomError } from "../CustomError.js";
import { checkEveryParamExistence } from "../helpers/middlewares.helper.js";

const createFightValid = (req, res, next) => {
const { fighter1, fighter2 } = req.body;
if (checkEveryParamExistence(fighter1, fighter2)) {
return next();
}
const paramsEroor = new CustomError(
MESSAGES.FIGHT_MESSAGES.ERROR_FIGHT_CREATE_PARAMS,
400
);
return next(paramsEroor);
};

const updateFightValid = (req, res, next) => {
const { fighter1Shot, fighter2Shot, fighter1Health, fighter2Health } =
req.body;
const id = req.params.id;
if (
checkEveryParamExistence(
fighter1Shot,
fighter2Shot,
fighter1Health,
fighter2Health,
id
)
) {
return next();
}
const requestedDataError = new CustomError(
MESSAGES.FIGHT_MESSAGES.ERROR_FIGHT_UPDATE_PARAMS,
400
);
return next(requestedDataError);
};

export { createFightValid, updateFightValid };
102 changes: 98 additions & 4 deletions middlewares/fighter.validation.middleware.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,107 @@
import { FIGHTER } from "../models/fighter.js";
import { fighterService } from "../services/fighterService.js";

const createFighterValid = (req, res, next) => {
const createFighterValid = async (req, res, next) => {
// TODO: Implement validatior for FIGHTER entity during creation
next();
const { name } = req.body;

try {
const fighter = await fighterService.getOneFighter({ name });

if (fighter) {
throw new Error(`Fighter with name '${name}' already exists.`);
}

const requiredFields = ["name", "power", "defense"];
checkRequiredFields(req.body, requiredFields);
checkValidKeys(req.body, Object.keys(FIGHTER));
isValid(req.body);

req.validatedData = { ...req.body };
next();
} catch (err) {
res.status(400).send({ error: err.message });
}
};

const updateFighterValid = (req, res, next) => {
const updateFighterValid = async (req, res, next) => {
// TODO: Implement validatior for FIGHTER entity during update
next();
const { id } = req.params;

try {
const fighter = await fighterService.getOneFighter({ id });

if (!fighter) {
return res.status(404).send({ error: `Fighter with id '${id}' was not found.` });
}

if (!Object.keys(req.body).length) {
throw new Error("No fields to update.");
}

if (req.body.name) {
const existing = await fighterService.getOneFighter({ name: req.body.name });
if (existing && existing.id !== id) {
throw new Error(`Fighter with name '${req.body.name}' already exists.`);
}
}

checkValidKeys(req.body, Object.keys(FIGHTER));
isValid(req.body);

req.validatedData = { ...req.body };
next();
} catch (err) {
res.status(400).send({ error: err.message });
}
};

const checkRequiredFields = (body, requiredFields) => {
requiredFields.forEach(field => {
if (body[field] === undefined || body[field] === null || body[field] === "") {
throw new Error(`Field '${field}' is required and cannot be empty.`);
}
});
};

const checkValidKeys = (body, validKeys) => {
Object.keys(body).forEach(key => {
if (!validKeys.includes(key)) {
throw new Error(`Invalid field '${key}' provided.`);
}
});
};

const isValid = (body) => {
if (body.name) {
validateName(body.name);
}
if (body.power !== undefined) {
validatePower(body.power);
}
if (body.defense !== undefined) {
validateDefense(body.defense);
}
};

const validateName = (name) => {
if (!/^[a-zA-Z]+$/.test(name)) {
throw new Error("Invalid fighter name. Only letters allowed.");
}
};

const validatePower = (power) => {
const num = Number(power);
if (isNaN(num) || num < 0 || num > 100) {
throw new Error("Power must be a number in the range 0 - 100.");
}
};

const validateDefense = (defense) => {
const num = Number(defense);
if (isNaN(num) || num < 1 || num > 10) {
throw new Error("Defense must be a number in the range 1 - 10.");
}
};

export { createFighterValid, updateFighterValid };
14 changes: 12 additions & 2 deletions middlewares/response.middleware.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
const responseMiddleware = (req, res, next) => {
// TODO: Implement middleware that returns result of the query
next();

if (res.err) {
const { message } = res.err;
console.log("message", message);
return res.json({
error: true,
message,
});
}
console.log("responseMiddleware res.data", res.data);
return res.json(res.data);
};

export { responseMiddleware };
export { responseMiddleware };
Loading