From 9e437999f7621334634b81ada1318486a761f7cb Mon Sep 17 00:00:00 2001 From: Kfir Dayan Date: Wed, 26 Apr 2023 19:32:22 +0300 Subject: [PATCH] replacing db to caching Hash Obj --- README.md | 24 +++++-------- package-lock.json | 12 +++++++ package.json | 1 + src/app.ts | 1 + src/handlers.ts | 92 +++++++++++++++++++++++++++++++++++++++++------ src/routes.ts | 2 +- 6 files changed, 105 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index f0f7977..9169135 100644 --- a/README.md +++ b/README.md @@ -25,22 +25,14 @@ init POSTGRES Database - CREATE DATABASE drop_shopping; create tables - - timeSlots - - CREATE TABLE time_slots ( - id SERIAL PRIMARY KEY, - start_time TIME NOT NULL, - end_time TIME NOT NULL, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() - ); - - holidays - - CREATE TABLE holidays ( - id SERIAL PRIMARY KEY, - holiday_date DATE NOT NULL, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() - ); + CREATE TABLE deliveries ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + slot_id INTEGER NOT NULL, + delivery_date DATE NOT NULL, + address TEXT NOT NULL, + status VARCHAR(10) NOT NULL +); knex for migration files diff --git a/package-lock.json b/package-lock.json index b324a5c..592c53b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ }, "devDependencies": { "@types/node": "^18.16.0", + "@types/pg": "^8.6.6", "nodemon": "^2.0.22", "ts-node-dev": "^2.0.0", "typescript": "^5.0.4" @@ -134,6 +135,17 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.0.tgz", "integrity": "sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==" }, + "node_modules/@types/pg": { + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz", + "integrity": "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", diff --git a/package.json b/package.json index b5ee35a..531253f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "devDependencies": { "@types/node": "^18.16.0", + "@types/pg": "^8.6.6", "nodemon": "^2.0.22", "ts-node-dev": "^2.0.0", "typescript": "^5.0.4" diff --git a/src/app.ts b/src/app.ts index 7d3f4e2..6dad687 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,5 @@ import express from 'express'; +const stam = 'fds' import routes from './routes'; const app = express(); diff --git a/src/handlers.ts b/src/handlers.ts index 86dab42..f1a66a5 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -1,42 +1,114 @@ import { Request, Response } from 'express'; import { resolveAddress } from './geocoding'; import { Address, AvailableTimeslots } from './types'; +import { randomUUID } from 'crypto'; // DOME import { getAvailableTimeSlots } from './services/timeslotsService'; import { getHolidays } from './services/holidaysService'; +const env = require('dotenv'); + +// create a hashing for caching delivery slots. this needs to be a caching db. +const deliveriesCache = new Map(); +// create a hashing for caching timeslots. this needs to be a caching db. +const slotsInUse = new Map(); + export const resolveAddressHandler = (req: Request, res: Response) => { - console.info("resolveAddressHandler called"); if (!req.body.searchTerm) { res.status(400).json({ error: 'Missing searchTerm' }); return; } const address: Promise
= resolveAddress(req.body.searchTerm); address.then((result) => { - console.info("resolveAddressHandler result: ", result); res.status(200).json(result); }) }; export const timeslotsHandler = async (req: Request, res: Response) => { - // TODO: Implement timeslots functionality if (!req.body.address) { res.status(400).json({ error: 'Missing address' }); return; } const address: Address = req.body.address; const timeSlots: AvailableTimeslots[] = await availableTimeSlots(address); - const availableTimeSlotsResult = await filterOutHolidaysByCountryCode(address, timeSlots); + 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); + } + }); res.status(200).json(availableTimeSlotsResult); }; export const deliveriesHandler = (req: Request, res: Response) => { - // TODO: Implement deliveries functionality - + 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 + // 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 + const delivery = { + _id: deliveryId, + userId, + slotId, + deliveryCreatedDate: new Date() + }; + if (slotsInUse.has(slotId)) { + slotsInUse.get(slotId).push(deliveryId); + } else { + slotsInUse.set(slotId, [deliveryId]); + } + deliveriesCache.set(deliveryId, delivery); + + console.log("deliveriesCache", deliveriesCache) + console.log("slotsInUse", slotsInUse) + res.status(200).json(delivery); }; + export const cancelDeliveryHandler = (req: Request, res: Response) => { // TODO: Implement cancel delivery functionality + // 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); + } + res.status(200).json(delivery); }; export const dailyDeliveriesHandler = (req: Request, res: Response) => { @@ -49,18 +121,17 @@ export const weeklyDeliveriesHandler = (req: Request, res: Response) => { -async function filterOutHolidaysByCountryCode(address: Address, availableTimeSlot: AvailableTimeslots[]) { +async function filterOutHolidaysByCountryCode(address: Address, availableTimeSlot: AvailableTimeslots[]): Promise { const countryCode = address.code; const holidays = await getHolidays(); const filteredAvailableTimeSlot = []; - console.log(availableTimeSlot) holidays.forEach((holiday) => { - if(holiday.country === countryCode) { + if (holiday.country === countryCode) { availableTimeSlot.forEach((timeSlot) => { - if(timeSlot.start_time.split(' ')[0] !== holiday.date) { + if (timeSlot.start_time.split(' ')[0] !== holiday.date) { filteredAvailableTimeSlot.push(timeSlot) } }) @@ -77,6 +148,7 @@ async function availableTimeSlots(address: Address) { for (const timeslot of timeslots.courier_available_timeslots) { if (timeslot.supported_postcodes.includes(address.postcode)) { availableTimeSlot.push({ + id: timeslot.id, start_time: timeslot.start_time, end_time: timeslot.end_time }); diff --git a/src/routes.ts b/src/routes.ts index 112da50..fe52b1c 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -4,10 +4,10 @@ import { resolveAddressHandler, timeslotsHandler, deliveriesHandler, cancelDeliv const router = express.Router(); router.post('/resolve-address', resolveAddressHandler); +router.delete('/deliveries/:deliveryId', cancelDeliveryHandler); router.post('/timeslots', timeslotsHandler); router.get('/deliveries/daily', dailyDeliveriesHandler); router.get('/deliveries/weekly', weeklyDeliveriesHandler); router.post('/deliveries', deliveriesHandler); -router.delete('/deliveries/:id', cancelDeliveryHandler); export default router; \ No newline at end of file