delivery_system/src/handlers.ts

208 lines
6.5 KiB
TypeScript
Raw Normal View History

import { Request, Response } from 'express';
2023-04-26 17:41:38 +00:00
import dateModule from 'date-and-time';
import { resolveAddress } from './geocoding';
import { Address, AvailableTimeslots, Orders } from './types';
2023-04-26 16:32:22 +00:00
import { randomUUID } from 'crypto';
2023-04-24 23:00:39 +00:00
import { getAvailableTimeSlots } from './services/timeslotsService';
import { getHolidays } from './services/holidaysService';
const dateFormat = 'YYYY-MM-DD HH:MM:SS';
2023-04-26 16:32:22 +00:00
2023-04-26 17:41:38 +00:00
// create a hashing for caching delivery slots.
2023-04-26 16:32:22 +00:00
const deliveriesCache = new Map();
2023-04-26 17:41:38 +00:00
// create a hashing for caching timeslots.
2023-04-26 16:32:22 +00:00
const slotsInUse = new Map();
2023-04-26 17:41:38 +00:00
// orders by dates caching.
const ordersByDates = new Map();
2023-04-26 16:32:22 +00:00
export const resolveAddressHandler = (req: Request, res: Response) => {
2023-04-26 10:35:39 +00:00
if (!req.body.searchTerm) {
res.status(400).json({ error: 'Missing searchTerm' });
return;
}
2023-04-24 18:49:20 +00:00
const address: Promise<Address> = resolveAddress(req.body.searchTerm);
address.then((result) => {
res.status(200).json(result);
2023-04-26 17:45:05 +00:00
return;
2023-04-26 10:35:39 +00:00
})
};
export const timeslotsHandler = async (req: Request, res: Response): Promise<void> => {
2023-04-26 10:35:39 +00:00
if (!req.body.address) {
res.status(400).json({ error: 'Missing address' });
return;
}
const address: Address = req.body.address;
const timeSlots: AvailableTimeslots[] = await availableTimeSlots(address);
2023-04-26 16:32:22 +00:00
const availableTimeSlotsResult: AvailableTimeslots[] = await filterOutHolidaysByCountryCode(address, timeSlots);
availableTimeSlotsResult.forEach((timeSlot, index) => {
if (slotsInUse.has(timeSlot.id) && slotsInUse.get(timeSlot.id).length >= 2) {
availableTimeSlotsResult.splice(index, 1);
}
});
2023-04-26 10:35:39 +00:00
res.status(200).json(availableTimeSlotsResult);
2023-04-26 17:45:05 +00:00
return;
};
2023-04-26 10:35:39 +00:00
export const deliveriesHandler = (req: Request, res: Response): void => {
2023-04-26 16:32:22 +00:00
const userId = req.body.userId;
const slotId = req.body.timeslotId;
// Idea: validate userId and slotId needed
if (!userId || !slotId) {
res.status(400).json({ error: 'Missing userId or slotId' });
return;
}
// Idea: check if user has already booked a delivery - is this needed?
2023-04-26 16:32:22 +00:00
// if (deliveriesCache.has(userId) && deliveriesCache.get(userId).slotId === slotId) {
// res.status(400).json({ error: 'User has already booked a delivery' });
// return;
// }
// Idea: check if timeslot is already full in cache
if (slotsInUse.has(slotId) && slotsInUse.get(slotId).length >= 2) {
res.status(400).json({ error: 'This timeslot is already full' });
return;
}
const deliveryId = randomUUID();
// Idea: create new delivery for user
// the date needs to be in "2023-09-18 14:00:00" format
2023-04-26 17:41:38 +00:00
const deliveryCreatedDate = dateModule.format(new Date(), dateFormat);
const date = deliveryCreatedDate.split(' ')[0];
2023-04-26 16:32:22 +00:00
const delivery = {
_id: deliveryId,
userId,
slotId,
2023-04-26 17:41:38 +00:00
deliveryCreatedDate: deliveryCreatedDate
2023-04-26 16:32:22 +00:00
};
2023-04-26 17:41:38 +00:00
// INFO: This is a very simple implementation of caching.
// INFO: In my opinion, transaction should be used to avoid data inconsistency.
2023-04-26 16:32:22 +00:00
if (slotsInUse.has(slotId)) {
slotsInUse.get(slotId).push(deliveryId);
} else {
slotsInUse.set(slotId, [deliveryId]);
}
2023-04-26 17:41:38 +00:00
if (ordersByDates.has(date)) {
ordersByDates.get(date).push(deliveryId);
} else {
ordersByDates.set(date, [deliveryId]);
}
2023-04-26 16:32:22 +00:00
deliveriesCache.set(deliveryId, delivery);
2023-04-26 17:41:38 +00:00
console.log("ordersByDates", ordersByDates)
2023-04-26 16:32:22 +00:00
console.log("deliveriesCache", deliveriesCache)
console.log("slotsInUse", slotsInUse)
2023-04-26 17:41:38 +00:00
2023-04-26 16:32:22 +00:00
res.status(200).json(delivery);
2023-04-26 17:45:05 +00:00
return;
};
2023-04-26 16:32:22 +00:00
export const cancelDeliveryHandler = (req: Request, res: Response): void => {
// TODO: Implement cancel delivery functionality
2023-04-26 16:32:22 +00:00
// DELETE /deliveries/:deliveryId
const deliveryId = req.params.deliveryId;
if (!deliveryId) {
res.status(400).json({ error: 'Missing deliveryId' });
return;
}
// remove from deliveriesCache and extract slotId
const delivery = deliveriesCache.get(deliveryId);
if (!delivery) {
res.status(400).json({ error: 'Delivery not found' });
return;
}
const slotId = delivery.slotId;
deliveriesCache.delete(deliveryId);
// remove from slotsInUse by slotId
const slot = slotsInUse.get(slotId);
const index = slot.indexOf(deliveryId);
if (index > -1) {
slot.splice(index, 1);
}
2023-04-26 17:45:05 +00:00
2023-04-26 16:32:22 +00:00
res.status(200).json(delivery);
2023-04-26 17:45:05 +00:00
return;
};
export const dailyDeliveriesHandler = (req: Request, res: Response): void => {
// TODO: Implement daily deliveries functionality
2023-04-26 17:41:38 +00:00
// GET /deliveries/daily - retrieve all todays deliveries - by today in ordersByDates
const date = dateModule.format(new Date(), dateFormat).split(' ')[0];
const todaysOrders: Orders[] = [];
2023-04-26 17:41:38 +00:00
if (ordersByDates.has(date)) {
ordersByDates.get(date).forEach((orderId) => {
todaysOrders.push(deliveriesCache.get(orderId));
});
}
res.status(200).json(todaysOrders);
2023-04-26 17:45:05 +00:00
return;
};
export const weeklyDeliveriesHandler = (req: Request, res: Response): void => {
// TODO: Implement weekly deliveries functionality
2023-04-26 17:41:38 +00:00
// GET /deliveries/weekly - retrieve all week deliveries - from today to 7 days later in ordersByDates
// this array will contain 7 elements - each element will be dates in the next 7 days
// ["today", "tomorrow" ... ]
const today = new Date();
const weeklyOrders: Orders [] = [];
2023-04-26 17:41:38 +00:00
for (let i = 0; i < 7; i++) {
const date = dateModule.format(new Date(today.setDate(today.getDate() + i)), dateFormat).split(' ')[0];
if (ordersByDates.has(date)) {
weeklyOrders.push(deliveriesCache.get(ordersByDates.get(date)));
}
}
res.status(200).json(weeklyOrders);
2023-04-26 17:45:05 +00:00
return;
};
2023-04-26 10:35:39 +00:00
2023-04-26 16:32:22 +00:00
async function filterOutHolidaysByCountryCode(address: Address, availableTimeSlot: AvailableTimeslots[]): Promise<AvailableTimeslots[]> {
2023-04-26 10:35:39 +00:00
const countryCode = address.code;
const holidays = await getHolidays();
const filteredAvailableTimeSlot = [];
console.log(availableTimeSlot)
holidays.forEach((holiday) => {
2023-04-26 16:32:22 +00:00
if (holiday.country === countryCode) {
2023-04-26 10:35:39 +00:00
availableTimeSlot.forEach((timeSlot) => {
2023-04-26 16:32:22 +00:00
if (timeSlot.start_time.split(' ')[0] !== holiday.date) {
2023-04-26 10:35:39 +00:00
filteredAvailableTimeSlot.push(timeSlot)
}
})
}
});
return filteredAvailableTimeSlot;
}
2023-04-26 17:41:38 +00:00
async function availableTimeSlots(address: Address): Promise<AvailableTimeslots[]> {
2023-04-26 10:35:39 +00:00
const availableTimeSlot = [];
const timeslots = await getAvailableTimeSlots();
// check by postcode if any available timeslots
// the time needs to be in "2023-09-18 14:00:00" format
2023-04-26 10:35:39 +00:00
for (const timeslot of timeslots.courier_available_timeslots) {
if (timeslot.supported_postcodes.includes(address.postcode)) {
availableTimeSlot.push({
2023-04-26 16:32:22 +00:00
id: timeslot.id,
2023-04-26 10:35:39 +00:00
start_time: timeslot.start_time,
end_time: timeslot.end_time
});
}
}
return availableTimeSlot;
}