From 09615983646312b5838d75e284c5c4671fc054af Mon Sep 17 00:00:00 2001 From: Abdelrahman Mohammed Date: Sat, 9 Nov 2024 00:30:50 +0200 Subject: [PATCH] feat: currency converter --- backend/.env.example | 4 +- .../src/controllers/itinerary.controller.ts | 12 +++++- backend/src/controllers/museum.controller.ts | 30 ++++++++++++- backend/src/controllers/product.controller.ts | 15 ++++++- backend/src/services/currencyConverter.ts | 42 +++++++++++++++++++ .../src/types/ResponseStatusCodes.types.ts | 1 + 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 backend/src/services/currencyConverter.ts diff --git a/backend/.env.example b/backend/.env.example index aba2c82..39e7eed 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -8,4 +8,6 @@ MONGO_URI=mongodb+srv://:@/ TOKEN_SECRET=your_token_secret # Cloudinary Configuration -CLOUDINARY_URL=cloudinary:// { try { @@ -23,9 +24,18 @@ const getItineraries = async (req: Request, res: Response) => { itineraries = itineraries.filter((itinerary) => !itinerary.flagged && itinerary.active); } + const currency: string = await currencyConverterService.getRequestCurrency(req); + itineraries = await Promise.all( + itineraries.map(async (itinerary) => { + itinerary.price = await currencyConverterService.convertPrice(itinerary.price, currency); + return itinerary; + }) + ); + const response = { message: 'Itineraries fetched successfully', data: { itineraries: itineraries }, + currency: currency, }; res.status(ResponseStatusCodes.OK).json(response); @@ -103,7 +113,7 @@ const deleteItinerary = async (req: Request, res: Response) => { const ItineraryId: string = req.params.id; if (await BookingRepo.checkItineraryBooked(ItineraryId)) { - res.status(402).json({ message: 'Cannot delete Itinerary as it is already booked' }); + res.status(ResponseStatusCodes.FORBIDDEN).json({ message: 'Cannot delete Itinerary as it is already booked' }); } const deleteRes = await ItineraryRepo.deleteItinerary(ItineraryId); const response = { diff --git a/backend/src/controllers/museum.controller.ts b/backend/src/controllers/museum.controller.ts index f4f64c6..443a517 100644 --- a/backend/src/controllers/museum.controller.ts +++ b/backend/src/controllers/museum.controller.ts @@ -3,13 +3,41 @@ import MuseumRepo from '../database/repositories/museum.repo'; import { logger } from '../middlewares/logger.middleware'; import { ResponseStatusCodes } from '../types/ResponseStatusCodes.types'; import mongoose from 'mongoose'; +import currencyConverterService from '../services/currencyConverter'; const getAllMuseums = async (req: Request, res: Response) => { try { - const museums = await MuseumRepo.getAllMuseums(); + let museums = await MuseumRepo.getAllMuseums(); + + const currency: string = await currencyConverterService.getRequestCurrency(req); + museums = await Promise.all( + museums.map(async (museum) => { + if (museum.ticket_prices?.foreigner) { + museum.ticket_prices.foreigner = await currencyConverterService.convertPrice( + museum.ticket_prices?.foreigner, + currency + ); + } + if (museum.ticket_prices?.native) { + museum.ticket_prices.native = await currencyConverterService.convertPrice( + museum.ticket_prices?.native, + currency + ); + } + if (museum.ticket_prices?.student) { + museum.ticket_prices.student = await currencyConverterService.convertPrice( + museum.ticket_prices?.student, + currency + ); + } + return museum; + }) + ); + const response = { message: 'Museums fetched successfully', data: { museums: museums }, + currency: currency, }; res.status(ResponseStatusCodes.OK).json(response); diff --git a/backend/src/controllers/product.controller.ts b/backend/src/controllers/product.controller.ts index daa2547..665c124 100644 --- a/backend/src/controllers/product.controller.ts +++ b/backend/src/controllers/product.controller.ts @@ -8,6 +8,7 @@ import { ProductType } from '../types/Product.types'; import productRepo from '../database/repositories/product.repo'; import { ReviewType } from '../types/Review.types'; +import currencyConverterService from '../services/currencyConverter'; const findProductById = async (req: Request, res: Response) => { try { @@ -44,11 +45,23 @@ const deleteProduct = async (req: Request, res: Response) => { const getProducts = async (req: Request, res: Response) => { try { - const products = await productRepo.getProducts(); + let products = await productRepo.getProducts(); + + const currency: string = await currencyConverterService.getRequestCurrency(req); + products = await Promise.all( + products.map(async (product) => { + if (!product.price) { + product.price = 0; + } + product.price = await currencyConverterService.convertPrice(product.price, currency); + return product; + }) + ); const response = { message: 'Products fetched successfully', data: { products }, + currency: currency, }; res.status(ResponseStatusCodes.OK).json(response); diff --git a/backend/src/services/currencyConverter.ts b/backend/src/services/currencyConverter.ts new file mode 100644 index 0000000..33323e5 --- /dev/null +++ b/backend/src/services/currencyConverter.ts @@ -0,0 +1,42 @@ +import { Request } from 'express'; + +const backUpRates: { [key: string]: number } = { + // As updated as possible in case the API fails + EGP: 1, + USD: 49.3, + EUR: 52.81, + GBP: 63.65, +}; + +const validCurrencies: string[] = ['EGP', 'USD', 'EUR', 'GBP']; + +export default class currencyConverterService { + static async getRequestCurrency(req: Request): Promise { + if (req.body.currency) { + if (validCurrencies.includes(req.body.currency)) { + return req.body.currency; + } + } + return 'EGP'; + } + + static async getConversionRate(to: string, from: string = 'EGP'): Promise { + try { + const response = await fetch(`https://api.exchangeratesapi.io/latest?base=${from}`); + const data = await response.json(); + return data.rates[to]; + } catch (error) { + return backUpRates[to]; + } + } + + static async convertPrices(prices: number[], to: string, from: string = 'EGP'): Promise { + const conversionRate = await this.getConversionRate(to, from); + return prices.map((price) => price / conversionRate); + } + + static async convertPrice(price: number, to: string, from: string = 'EGP'): Promise { + const conversionRate = await this.getConversionRate(to, from); + return price / conversionRate; + } +} diff --git a/backend/src/types/ResponseStatusCodes.types.ts b/backend/src/types/ResponseStatusCodes.types.ts index fc54a80..c5972c1 100644 --- a/backend/src/types/ResponseStatusCodes.types.ts +++ b/backend/src/types/ResponseStatusCodes.types.ts @@ -4,6 +4,7 @@ export const enum ResponseStatusCodes { NO_CONTENT = 204, BAD_REQUEST = 400, UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, FORBIDDEN = 403, NOT_FOUND = 404, INTERNAL_SERVER_ERROR = 500,