Skip to content

Commit

Permalink
feat: currency converter
Browse files Browse the repository at this point in the history
  • Loading branch information
Sherlemious committed Nov 8, 2024
1 parent d27f815 commit 0961598
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 4 deletions.
4 changes: 3 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ MONGO_URI=mongodb+srv://<username>:<password>@<cluster-url>/<database>
TOKEN_SECRET=your_token_secret

# Cloudinary Configuration
CLOUDINARY_URL=cloudinary://<api
CLOUDINARY_URL=cloudinary://<api

# Exchange API Access Key
12 changes: 11 additions & 1 deletion backend/src/controllers/itinerary.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ResponseStatusCodes } from '../types/ResponseStatusCodes.types';
import { accountType } from '../types/User.types';
import ItineraryRepo from '../database/repositories/itinerary.repo';
import BookingRepo from '../database/repositories/booking.repo';
import currencyConverterService from '../services/currencyConverter';

const getItineraries = async (req: Request, res: Response) => {
try {
Expand All @@ -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);
Expand Down Expand Up @@ -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 = {
Expand Down
30 changes: 29 additions & 1 deletion backend/src/controllers/museum.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 14 additions & 1 deletion backend/src/controllers/product.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
42 changes: 42 additions & 0 deletions backend/src/services/currencyConverter.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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<number> {
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<number[]> {
const conversionRate = await this.getConversionRate(to, from);
return prices.map((price) => price / conversionRate);
}

static async convertPrice(price: number, to: string, from: string = 'EGP'): Promise<number> {
const conversionRate = await this.getConversionRate(to, from);
return price / conversionRate;
}
}
1 change: 1 addition & 0 deletions backend/src/types/ResponseStatusCodes.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 0961598

Please sign in to comment.