Compare commits

...

12 Commits

Author SHA1 Message Date
7fead5486b fix(db): modify 'rents' SQL table structure
The commit modifies the 'rents' SQL table:
- Replaces the improperly quoted table name with correct syntax: from 'rents' to `rents`.
- Changes `id` field data type from tinyint to varchar.
- Updates database dump's completion timestamp.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 14:56:37 +02:00
5cc214a29b schema
Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-03 14:55:07 +02:00
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
10 changed files with 180 additions and 20 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>

6
db.sql
View File

@@ -77,7 +77,7 @@ CREATE TABLE `models` (
-- Table structure for table `rents`
--
DROP TABLE IF EXISTS 'rents';
DROP TABLE IF EXISTS `rents`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `rents` (
@@ -88,7 +88,7 @@ CREATE TABLE `rents` (
`eat` date NOT NULL,
`need_survey` tinyint(1) DEFAULT NULL,
`km_at_start` int(11) DEFAULT NULL,
`id` tinyint(1) NOT NULL,
`id` varchar(36) NOT NULL,
PRIMARY KEY (`id`),
KEY `rent_vehicles_id_fk` (`vehicle_id`),
KEY `rent_users_id_fk` (`user_id`),
@@ -152,4 +152,4 @@ CREATE TABLE `vehicles` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2024-04-29 14:54:00
-- Dump completed on 2024-05-03 14:56:07

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 198 KiB

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);

View File

@@ -4,7 +4,6 @@ import { isDebugMode } from "@utils/debugState";
import { Logger } from "tslog";
import { v4 } from "uuid";
import IDbRent from "@interfaces/database/IDbRent";
import {body} from "express-validator";
const logger = new Logger({
name: "RentService",
@@ -33,6 +32,10 @@ async function createRentService(data: IDbRent): Promise<boolean> {
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(),
@@ -146,6 +149,9 @@ async function deleteRentService(rentId: string) {
return true;
}
return false;
} catch (error) {
logger.error(`\n\n> Error deleting rent: \n${error}\n`);
return false
}
}