Compare commits

...

12 Commits

Author SHA1 Message Date
65a6ae2e3c feat(routes): update rent related routes
- The "/affected" route has been updated to now use the `RentController.getAssignedToUser` method.
- The "/affected/all" route, which was previously not implemented, now utilizes the `RentController.getAll` function.
- These changes will help facilitate the retrieval of individual and all rented vehicles.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:18:46 +02:00
83d07a2812 feat(services): add availability check in rent service
A new condition was added in `rent.service.ts` to check if a vehicle is available before executing the rent operation. It also logs an error if the vehicle is not available.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:18:33 +02:00
e8acfd7b30 feat(services): update condition checks in model.service.ts
Updated the condition checks in the 'delete' and 'fetch' methods of the `model.service.ts`. Now, it properly checks if the model exists by examining the first item of the result array, instead of considering the result array itself. This resolves issues where fetching or deleting a model with a non-existent slug would incorrectly attempt to perform operations considering the empty array as valid input.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:18:15 +02:00
438ae4b5d0 fix(controllers): update model slug in updateModel function
The model slug has been updated to use `req.params["modelSlug"]` instead of `body.slug_name` in the `updateModel` function, ensuring the correct slug is used when checking if the model exists.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:17:58 +02:00
6ccc899320 feat(interfaces): add isAvailable field to IDbVehicle interface
The IDbVehicle interface has been updated to include an optional `isAvailable` field. This field can be used to check the availability status of a vehicle.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:17:47 +02:00
5afb6e22bf feat(services): update getBySlug function in MySQL services
The `getBySlug` function originally returned a single database model by slug from the MySQL handler. It has been updated to return a list of database models by slug name. Error handling for failed query executions is also included.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:17:37 +02:00
bdba2f6e29 feat(controllers): add rent controller
- Implemented functionality for creating, updating, and getting rent information.
- Created `createRent`, `updateRent`, `getAllAssigned`, and `getAssignedToUser` functions in the `rent.controller.ts` file.
- These functions handle all the interactions related to renting vehicles.

Issue: #25
Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 13:17:25 +02:00
30c6f2e3f1 feat(services): remove unused import from rent.service.ts
The `body` import from "express-validator" has been removed from rent.service.ts as it was not used. Also, an error handler for deleting rent was updated to always return false on encountering an error, improving the error handling mechanism.

Issue: #24
Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 12:02:36 +02:00
b47ec6c440 feat(services): add error logging in rent.service
A logging feature is added in the `rent.service.ts` to log any errors when attempting to delete rent. This feature helps in diagnosing issues during rent deletion.

Issue: #24
Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 12:01:56 +02:00
b4f200cb32 feat(inspectionProfiles): increase TypeScript code redundancy threshold
The minimum size for the TypeScript language in the duplicated code check has been increased. This update reduces the sensitivity of the code redundancy inspection, potentially reducing false positive results.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 12:01:03 +02:00
ec53fcb247 feat(services): add rent service functions
This commit introduces new functions to handle rental services in the src/services/rent.service.ts. Functions created handle rental CRUD operations such as createRentService, updateRentService, deleteRentService, also additional utility functions such as getUserRentService and getRentByIdService that deal with user-associated rentals were introduced. These new functions serve to simplify rental management and improve our services for rental handling.

Issue: #24
Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 12:00:48 +02:00
371d960cf3 feat(services): update getById method in mysql.service
- Update the getById method in mysql.service to return an array of vehicles instead of a single vehicle
- Add a new getById method to retrieve array of rental information by rental ID
- Improve the documentation to match the modifications in both methods' functionalities

Issue: #23
Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 11:59:58 +02:00
8 changed files with 383 additions and 22 deletions

View File

@@ -3,7 +3,7 @@
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="114" name="TypeScript" />
<language minSize="174" name="TypeScript" />
</Languages>
</inspection_tool>
</profile>

View File

@@ -42,7 +42,7 @@ async function createModel(req: Request, res: Response): Promise<Response> {
async function updateModel(req: Request, res: Response): Promise<Response> {
const body: IDbModel = req.body;
const doesExist = await ModelService.getBySlug(`${body.slug_name}`);
const doesExist = await ModelService.getBySlug(`${req.params["modelSlug"]}`);
if (!doesExist) {
logger.error("Model does not exist");
return res.status(404).json({

View File

@@ -0,0 +1,150 @@
import { isDebugMode } from "@utils/debugState";
import type { Request, Response } from "express";
import { Logger } from "tslog";
import RentService from "@services/rent.service";
import {HttpStatusCode} from "@interfaces/requests/HttpStatusCode";
import type IDbRent from "@interfaces/database/IDbRent";
import JwtService from "@services/jwt.service";
import UserService from "@services/user.service";
const logger = new Logger({
name: "RentController",
});
async function createRent(req: Request, res: Response): Promise<Response> {
try {
const rentData: IDbRent = req.body;
if (!rentData.active || !rentData.need_survey || !rentData.eat || !rentData.iat || !rentData.user_id || !rentData.vehicle_id || !rentData.km_at_start || !rentData.active) {
logger.error("Invalid rent data");
return res.status(HttpStatusCode.BadRequest).json({
error: "Invalid rent data",
});
}
const rent = await RentService.create(rentData);
logger.info(`\n\n> Rent created successfully! (ID: ${rentData.vehicle_id})\n`);
return res.status(201).json({
message: "Rent created successfully",
rent,
});
} catch (error) {
logger.error(`\n\n> Failed to create rent !\n${error}\n`);
return res.status(500).json({
error: "Failed to create rent",
});
}
}
async function updateRent(req: Request, res: Response): Promise<Response> {
const body: IDbRent = req.body;
if (!body.vehicle_id || !body.user_id || !body.active || !body.need_survey || !body.eat || !body.iat || !body.km_at_start) {
logger.error("Invalid rent data");
return res.status(HttpStatusCode.BadRequest).json({
error: "Invalid rent data",
});
}
const rentId = req.params["rentId"];
if (!rentId || rentId.length !== 36) {
logger.error("Invalid rent ID");
return res.status(HttpStatusCode.BadRequest).json({
error: "Invalid rent ID",
});
}
const result = await RentService.update({
id: rentId,
vehicle_id: ``,
user_id: ``,
active: !!body.active,
need_survey: !!body.need_survey,
eat: body.eat,
iat: body.iat,
km_at_start: body.km_at_start,
})
if (!result) {
logger.error(`Failed to update rent with ID: ${rentId}`);
return res.status(HttpStatusCode.InternalServerError).json({
error: `Failed to update rent with ID: ${rentId}`,
});
}
logger.info(`Rent with ID: ${rentId} updated successfully`);
return res.status(HttpStatusCode.Ok).json({
message: `Rent with ID: ${rentId} updated successfully`,
});
}
async function getAllAssigned(res: Response): Promise<Response> {
const rents = await RentService.getAll();
if (rents.length === 0) {
return res.status(HttpStatusCode.NotFound).json({
error: "No assigned rents found",
});
}
return res.status(HttpStatusCode.Ok).json({
iat: Date.now(),
rents: rents,
total: rents.length
});
}
async function getAssignedToUser(req: Request, res: Response): Promise<Response> {
const authHeader = req.headers.authorization;
const bearerToken = authHeader?.split(" ")[1];
if (!bearerToken) {
logger.warn(`Bearer token not provided (${req.ip})`);
return res
.type("application/json")
.status(HttpStatusCode.Unauthorized)
.json({
error: "Unauthorized",
});
}
const payload = await JwtService.verify(bearerToken);
if (!payload || !payload.sub) {
logger.warn(`Unauthorized access attempt (${req.ip})`);
return res
.type("application/json")
.status(HttpStatusCode.Unauthorized)
.json({
error: "Unauthorized",
});
}
const sourceUser = await UserService.getFromId(payload.sub);
if (!sourceUser) {
return res.type("application/json").status(HttpStatusCode.ImATeapot).json({
error: "You dont exist anymore",
});
}
const userId: string = payload.sub;
if (!userId || userId.length !== 36) {
logger.error("Invalid user ID");
return res.status(HttpStatusCode.BadRequest).json({
error: "Invalid user ID",
});
}
let targetId = userId
if ("is_admin" in sourceUser && sourceUser.is_admin) {
targetId = req.body.targetId || userId
}
const rents = await RentService.getUserRent(targetId);
if (!rents) {
return res.status(HttpStatusCode.NotFound).json({
error: "No assigned rents found for the user",
});
}
return res.status(HttpStatusCode.Ok).json({
iat: Date.now(),
rents: rents,
total: rents.length,
});
}
if (isDebugMode()) logger.debug("\nController loaded.");
const RentController = {
create: createRent,
update: updateRent,
getAll: getAllAssigned,
getAssignedToUser,
};
export default RentController;

View File

@@ -4,4 +4,5 @@ export interface IDbVehicle {
model_id: string;
odometer: number;
health_state: number;
isAvailable?: boolean;
}

View File

@@ -2,14 +2,15 @@ import AdminGuard from "@validators/AdminGuard";
import UserGuard from "@validators/UserGuard";
import express, { type Router } from "express";
import VehicleController from "@controllers/vehicle.controller";
import RentController from "@controllers/rent.controller";
const RentRouter: Router = express.Router();
// Get rent affected to the user
RentRouter.route("/affected").get(UserGuard);
RentRouter.route("/affected").get(UserGuard, RentController.getAssignedToUser);
// Get all vehicle in rent (admin only) //TODO Non implemented yet
RentRouter.route("/affected/all").get(AdminGuard);
// Get all vehicle in rent (admin only)
RentRouter.route("/affected/all").get(AdminGuard, RentController.getAll);
// Add a new vehicle (admin only)
RentRouter.route("/veh/new").post(AdminGuard, VehicleController.create);

View File

@@ -80,12 +80,14 @@ async function deleteModel(modelSlug: string): Promise<boolean> {
}
logger.info(`Deleting model with ID: ${modelSlug}`);
const doesExist = await MysqlService.Model.getBySlug(DbHandler, modelSlug);
if (!doesExist || !doesExist.id) {
if (!doesExist[0]) {
logger.warn(`Model with slug ${modelSlug} not found`);
return false;
}
const target = doesExist[0]
if (!target.id) return false;
try {
await MysqlService.Model.delete(DbHandler, doesExist.id);
await MysqlService.Model.delete(DbHandler, target.id);
logger.info("Deletion Successful !");
return true;
} catch (error) {
@@ -104,11 +106,11 @@ async function getBySlugModel(modelSlug: string): Promise<IDbModel | null> {
logger.info(`Fetching model with slug: ${modelSlug}`);
try {
const model = await MysqlService.Model.getBySlug(DbHandler, modelSlug);
if (!model) {
if (!model[0]) {
logger.warn(`Model with slug ${modelSlug} not found`);
return null;
}
return model;
return model[0];
} catch (error) {
logger.error(`Error fetching model by slug: ${error}`);
return null;

View File

@@ -569,20 +569,20 @@ const MySqlService = {
},
/**
* Retrieves a database model by slug from a given MySQL handler.
* Retrieves a list of database models by slug name.
*
* @param {MysqlHandler} handler - The MySQL handler instance.
* @param {string} modelSlug - The slug of the model to retrieve.
* @return {Promise<IDbModel>} A promise that resolves with the retrieved model.
* @throws {Error} If there was an error executing the query.
* @param {MysqlHandler} handler - The MySQL handler used to execute the query.
* @param {string} modelSlug - The slug name of the model to retrieve.
* @return {Promise<Array<IDbModel>>} - A promise that resolves to an array of database models.
* @throws {Error} - If an error occurs during the execution of the query.
*/
getBySlug(handler: MysqlHandler, modelSlug: string): Promise<IDbModel> {
getBySlug(handler: MysqlHandler, modelSlug: string): Promise<Array<IDbModel>> {
return new Promise((resolve, reject) => {
const _sql = "SELECT * FROM models WHERE slug_name VALUES(?)";
const _values = [modelSlug];
try {
handler.execute(_sql, _values).then((result) => {
return resolve(result as unknown as IDbModel);
return resolve(result as unknown as Array<IDbModel>);
});
} catch (err: unknown) {
reject(err as Error);
@@ -795,14 +795,14 @@ const MySqlService = {
},
/**
* Retrieves a vehicle from the database by its ID.
* Retrieves an array of vehicles from the database by a given vehicle ID.
*
* @param {MysqlHandler} handler - The instance of the MySQL handler.
* @param {MysqlHandler} handler - The MySQL handler object for executing database queries.
* @param {string} vehicleId - The ID of the vehicle to retrieve.
* @returns {Promise<IDbVehicle>} - A promise that resolves to the retrieved vehicle.
* @throws {Error} - If an error occurs while retrieving the vehicle.
* @returns {Promise<Array<IDbVehicle>>} - A promise that resolves with an array of vehicles matching the ID.
* @throws {Error} - If there is an error executing the query or the ID is invalid.
*/
getById(handler: MysqlHandler, vehicleId: string): Promise<IDbVehicle> {
getById(handler: MysqlHandler, vehicleId: string): Promise<Array<IDbVehicle>> {
return new Promise((resolve, reject) => {
if (!vehicleId) return reject("Id is undefined");
if (vehicleId.length !== 36) return reject("Id invalid");
@@ -810,7 +810,7 @@ const MySqlService = {
const _values = [vehicleId];
try {
handler.execute(_sql, _values).then((result) => {
return resolve(result as unknown as IDbVehicle);
return resolve(result as unknown as Array<IDbVehicle>);
});
} catch (err: unknown) {
reject(err as Error);
@@ -1135,6 +1135,31 @@ const MySqlService = {
});
},
/**
* Retrieves rental information from the database by rental ID.
*
* @param {MysqlHandler} handler - The instance of the MySQL handler used to execute the query.
* @param {string} rentId - The ID of the rental to retrieve.
*
* @returns {Promise<Array<IDbRent>>} A promise that resolves with an array of rental information
* or rejects with an error message if the ID is undefined or invalid.
*/
getById(handler: MysqlHandler, rentId: string): Promise<Array<IDbRent>> {
return new Promise((resolve, reject) => {
if (!rentId) return reject("Id is undefined");
if (rentId.length !== 36) return reject("Id invalid");
const _sql = "SELECT * FROM rents WHERE id VALUES(?)";
const _values = [rentId];
try {
handler.execute(_sql, _values).then((result) => {
return resolve(result as unknown as Array<IDbRent>);
});
} catch (err: unknown) {
reject(err as Error);
}
});
},
//ToTest
/**
* Retrieves all assigned vehicles from the database.

View File

@@ -0,0 +1,182 @@
//import { ErrorType, type ISError } from "@interfaces/services/ISError";
import MySqlService from "@services/mysql.service";
import { isDebugMode } from "@utils/debugState";
import { Logger } from "tslog";
import { v4 } from "uuid";
import IDbRent from "@interfaces/database/IDbRent";
const logger = new Logger({
name: "RentService",
});
const DbHandler = new MySqlService.Handler("RentService");
async function createRentService(data: IDbRent): Promise<boolean> {
if (isDebugMode()) logger.debug(`\n\n> Creating a new rent...\n`);
const wantedVehicleId = data.id;
const targetUserId = data.user_id;
if (!targetUserId || !wantedVehicleId) {
logger.error(`Missing targetUserId or targetVehicleId`);
return false;
}
const userIfExist = await MySqlService.User.getById(DbHandler, targetUserId);
if (!userIfExist[0]) {
logger.error(`User does not exist`);
return false;
}
const targetUser = userIfExist[0]
if (!targetUser.id) return false;
const vehicleIfExist = await MySqlService.Vehicle.getById(DbHandler, wantedVehicleId);
if (!vehicleIfExist[0] || !vehicleIfExist[0].id) {
logger.error(`Vehicle does not exist`);
return false;
}
const vehicleId = vehicleIfExist[0].id
if (!vehicleIfExist[0].isAvailable) {
logger.error(`Vehicle is not available`);
return false;
}
try {
const result = await MySqlService.Rent.insert(DbHandler, {
id: v4(),
vehicle_id: vehicleId,
user_id: targetUser.id,
active: data.active,
iat: new Date(Date.parse(`${data.iat}`)),
eat: new Date(Date.parse(`${data.eat}`)),
need_survey: data.need_survey,
km_at_start: Number.parseInt(`${data.km_at_start}`)
});
if (result.affectedRows !== 0) {
logger.info("\n\n> Success !");
return true;
}
return false;
} catch (error) {
logger.error(`\n\n> Error creating category: \n${error}\n`);
return false;
}
}
async function updateRentService(data: IDbRent) {
if (isDebugMode()) logger.debug(`\n\n> Updating a rent...\n`);
const wantedVehicleId = data.id;
const targetUserId = data.user_id;
if (!targetUserId || !wantedVehicleId || !data.id) {
logger.error(`Missing targetUserId or targetVehicleId`);
return false;
}
const rentIfExist = await MySqlService.Rent.getById(DbHandler, data.id);
if (!rentIfExist[0]) {
logger.error(`Rent does not exist`);
return false;
}
const rentId = rentIfExist[0].id;
if (!rentId) {
logger.error(`RentId does not exist`);
return false;
}
const userIfExist = await MySqlService.User.getById(DbHandler, targetUserId);
if (!userIfExist[0]) {
logger.error(`User does not exist`);
return false;
}
const targetUser = userIfExist[0]
if (!targetUser.id) return false;
const vehicleIfExist = await MySqlService.Vehicle.getById(DbHandler, wantedVehicleId);
if (!vehicleIfExist[0] || !vehicleIfExist[0].id) {
logger.error(`Vehicle does not exist`);
return false;
}
const vehicleId = vehicleIfExist[0].id
try {
const result = await MySqlService.Rent.update(DbHandler, {
id: rentId,
vehicle_id: vehicleId,
user_id: targetUser.id,
active: data.active,
iat: new Date(Date.parse(`${data.iat}`)),
eat: new Date(Date.parse(`${data.eat}`)),
need_survey: data.need_survey,
km_at_start: Number.parseInt(`${data.km_at_start}`)
});
if (result.affectedRows !== 0) {
logger.info("\n\n> Success !");
return true;
}
return false;
} catch (error) {
logger.error(`\n\n> Error updating category: \n${error}\n`);
return false;
}
}
async function getAllAssignedRentService() {
const result = await MySqlService.Rent.getAllAssigned(DbHandler);
if (result.length > 0) {
return result;
}
logger.warn(`No assigned rents found`);
return [];
}
async function getRentByIdService(rentId: string) {
if (!rentId || rentId.length !== 36) {
logger.warn(`Id missing or not conform`)
return false;
}
const rent = await MySqlService.Rent.getById(DbHandler, rentId);
if (rent.length > 0) {
return rent[0];
}
logger.warn(`Rent not found`);
return null;
}
async function deleteRentService(rentId: string) {
const rentIfExist = await MySqlService.Rent.getById(DbHandler, rentId);
if (!rentIfExist[0]) {
logger.error(`Rent does not exist`);
return false;
}
const target = rentIfExist[0]
if (!target.id) return false;
try {
const result = await MySqlService.Rent.delete(DbHandler, target.id);
if (result.affectedRows !== 0) {
logger.info("\n\n> Success !");
return true;
}
return false;
} catch (error) {
logger.error(`\n\n> Error deleting rent: \n${error}\n`);
return false
}
}
async function getUserRentService(userId: string) {
if (!userId) {
logger.warn(`Missing userId`);
return false;
}
const rents = await MySqlService.Rent.getAssignedToUser(DbHandler, userId);
if (rents.length > 0) {
return rents;
}
logger.warn(`No rents found for user`);
return false;
}
if (isDebugMode()) logger.debug("\nService loaded.");
const VehicleService = {
create: createRentService,
update: updateRentService,
getAll: getAllAssignedRentService,
getById: getRentByIdService,
delete: deleteRentService,
getUserRent: getUserRentService,
};
export default VehicleService;