From 13f12577408bd42af6f2ed687b939ab0149debcd Mon Sep 17 00:00:00 2001 From: Patrick <50352812+Mueller-Patrick@users.noreply.github.com> Date: Thu, 20 May 2021 17:46:09 +0200 Subject: [PATCH] BETTERZON-113, BETTERZON-114, BETTERZON-115: Adding API endpoint for favorite shops (#61) --- Backend/src/index.ts | 2 + .../favorite_shops/favoriteshop.interface.ts | 5 + .../favorite_shops/favoriteshops.interface.ts | 5 + .../favorite_shops/favoriteshops.router.ts | 100 ++++++++++++++++++ .../favorite_shops/favoriteshops.service.ts | 92 ++++++++++++++++ .../models/pricealarms/pricealarms.router.ts | 4 +- .../models/pricealarms/pricealarms.service.ts | 20 +--- 7 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 Backend/src/models/favorite_shops/favoriteshop.interface.ts create mode 100644 Backend/src/models/favorite_shops/favoriteshops.interface.ts create mode 100644 Backend/src/models/favorite_shops/favoriteshops.router.ts create mode 100644 Backend/src/models/favorite_shops/favoriteshops.service.ts diff --git a/Backend/src/index.ts b/Backend/src/index.ts index b387dd9..0af240f 100644 --- a/Backend/src/index.ts +++ b/Backend/src/index.ts @@ -16,6 +16,7 @@ import {notFoundHandler} from './middleware/notFound.middleware'; import {usersRouter} from './models/users/users.router'; import {pricealarmsRouter} from './models/pricealarms/pricealarms.router'; import {contactpersonsRouter} from './models/contact_persons/contact_persons.router'; +import {favoriteshopsRouter} from './models/favorite_shops/favoriteshops.router'; const cookieParser = require('cookie-parser'); @@ -51,6 +52,7 @@ app.use('/users', usersRouter); app.use('/vendors', vendorsRouter); app.use('/pricealarms', pricealarmsRouter); app.use('/contactpersons', contactpersonsRouter); +app.use('/favoriteshops', favoriteshopsRouter); app.use(errorHandler); app.use(notFoundHandler); diff --git a/Backend/src/models/favorite_shops/favoriteshop.interface.ts b/Backend/src/models/favorite_shops/favoriteshop.interface.ts new file mode 100644 index 0000000..71652b1 --- /dev/null +++ b/Backend/src/models/favorite_shops/favoriteshop.interface.ts @@ -0,0 +1,5 @@ +export interface FavoriteShop { + favorite_id: number; + vendor_id: number; + user_id: number; +} diff --git a/Backend/src/models/favorite_shops/favoriteshops.interface.ts b/Backend/src/models/favorite_shops/favoriteshops.interface.ts new file mode 100644 index 0000000..b921b0d --- /dev/null +++ b/Backend/src/models/favorite_shops/favoriteshops.interface.ts @@ -0,0 +1,5 @@ +import {FavoriteShop} from './favoriteshop.interface'; + +export interface FavoriteShops { + [key: number]: FavoriteShop; +} diff --git a/Backend/src/models/favorite_shops/favoriteshops.router.ts b/Backend/src/models/favorite_shops/favoriteshops.router.ts new file mode 100644 index 0000000..0c0d033 --- /dev/null +++ b/Backend/src/models/favorite_shops/favoriteshops.router.ts @@ -0,0 +1,100 @@ +/** + * Required External Modules and Interfaces + */ + +import express, {Request, Response} from 'express'; +import * as FavoriteShopsService from './favoriteshops.service'; +import {FavoriteShop} from './favoriteshop.interface'; +import {FavoriteShops} from './favoriteshops.interface'; +import * as UserService from '../users/users.service'; + + +/** + * Router Definition + */ +export const favoriteshopsRouter = express.Router(); + + +/** + * Controller Definitions + */ + +//GET favoriteshops/ +favoriteshopsRouter.get('/', async (req: Request, res: Response) => { + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + const priceAlarms = await FavoriteShopsService.getFavoriteShops(user.user_id); + + res.status(200).send(priceAlarms); + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// POST favoriteshops/ +favoriteshopsRouter.post('/', async (req: Request, res: Response) => { + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + // Get info for price alarm creation + const vendor_id = req.body.vendor_id; + + if (!vendor_id) { + // Missing + res.status(400).send(JSON.stringify({message: 'Missing parameters'})); + return; + } + + // Create price alarm + const success = await FavoriteShopsService.createFavoriteShop(user.user_id, vendor_id); + + if (success) { + res.status(201).send(JSON.stringify({success: true})); + return; + } else { + res.status(500).send(JSON.stringify({success: false})); + return; + } + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// DELETE favoriteshops/ +favoriteshopsRouter.delete('/:id', async (req: Request, res: Response) => { + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + // Get info for price alarm creation + const favorite_id = parseInt(req.params.id, 10); + + if (!favorite_id) { + // Missing + res.status(400).send(JSON.stringify({message: 'Missing parameters'})); + return; + } + + // Create price alarm + const success = await FavoriteShopsService.deleteFavoriteShop(user.user_id, favorite_id); + + if (success) { + res.status(201).send(JSON.stringify({success: true})); + return; + } else { + res.status(500).send(JSON.stringify({success: false})); + return; + } + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); diff --git a/Backend/src/models/favorite_shops/favoriteshops.service.ts b/Backend/src/models/favorite_shops/favoriteshops.service.ts new file mode 100644 index 0000000..a4e1d12 --- /dev/null +++ b/Backend/src/models/favorite_shops/favoriteshops.service.ts @@ -0,0 +1,92 @@ +import * as dotenv from 'dotenv'; + +dotenv.config(); + +const mariadb = require('mariadb'); +const pool = mariadb.createPool({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + connectionLimit: 5 +}); + +/** + * Data Model Interfaces + */ + +import {FavoriteShop} from './favoriteshop.interface'; +import {FavoriteShops} from './favoriteshops.interface'; + + +/** + * Service Methods + */ + +/** + * Creates a favorite shop entry for the given user for the given shop + * @param user_id The id of the user to create the favorite shop entry for + * @param vendor_id The id of the vendor to set as favorite + */ +export const createFavoriteShop = async (user_id: number, vendor_id: number): Promise => { + let conn; + try { + conn = await pool.getConnection(); + const res = await conn.query('INSERT INTO favorite_shops (vendor_id, user_id) VALUES (?, ?)', [vendor_id, user_id]); + + return res.affectedRows === 1; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } +}; + +/** + * Fetches and returns all favorite shops for the given user + * @param user_id + */ +export const getFavoriteShops = async (user_id: number): Promise => { + let conn; + let shops = []; + try { + conn = await pool.getConnection(); + const rows = await conn.query('SELECT favorite_id, vendor_id, user_id FROM favorite_shops WHERE user_id = ?', user_id); + for (let row in rows) { + if (row !== 'meta') { + shops.push(rows[row]); + } + } + + return shops; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } +}; + +/** + * Deletes the given favorite shop entry + * @param user_id The id of the user that wants to delete the favorite shop entry + * @param favorite_id The favorite shop to delete + */ +export const deleteFavoriteShop = async (user_id: number, favorite_id: number): Promise => { + let conn; + try { + conn = await pool.getConnection(); + const res = await conn.query('DELETE FROM favorite_shops WHERE favorite_id = ? AND user_id = ?', [favorite_id, user_id]); + + return res.affectedRows === 1; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } +}; diff --git a/Backend/src/models/pricealarms/pricealarms.router.ts b/Backend/src/models/pricealarms/pricealarms.router.ts index 4ba1f9f..cc42032 100644 --- a/Backend/src/models/pricealarms/pricealarms.router.ts +++ b/Backend/src/models/pricealarms/pricealarms.router.ts @@ -35,7 +35,7 @@ pricealarmsRouter.get('/', async (req: Request, res: Response) => { } }); -// POST pricealarms/create +// POST pricealarms/ pricealarmsRouter.post('/', async (req: Request, res: Response) => { try { // Authenticate user @@ -68,7 +68,7 @@ pricealarmsRouter.post('/', async (req: Request, res: Response) => { } }); -// PUT pricealarms/update +// PUT pricealarms/ pricealarmsRouter.put('/', async (req: Request, res: Response) => { try { // Authenticate user diff --git a/Backend/src/models/pricealarms/pricealarms.service.ts b/Backend/src/models/pricealarms/pricealarms.service.ts index 279b05b..7d975fb 100644 --- a/Backend/src/models/pricealarms/pricealarms.service.ts +++ b/Backend/src/models/pricealarms/pricealarms.service.ts @@ -29,17 +29,13 @@ import {PriceAlarms} from './pricealarms.interface'; * @param product_id The id of the product to create the price alarm for * @param defined_price The defined price for the price alarm */ -export const createPriceAlarm = async (user_id: number, product_id: number, defined_price: number): Promise => { +export const createPriceAlarm = async (user_id: number, product_id: number, defined_price: number): Promise => { let conn; try { conn = await pool.getConnection(); const res = await conn.query('INSERT INTO price_alarms (user_id, product_id, defined_price) VALUES (?, ?, ?)', [user_id, product_id, defined_price]); - if (res.affectedRows === 1) { - return true; - } else { - return false; - } + return res.affectedRows === 1; } catch (err) { throw err; } finally { @@ -47,8 +43,6 @@ export const createPriceAlarm = async (user_id: number, product_id: number, defi conn.end(); } } - - return false; }; /** @@ -83,17 +77,13 @@ export const getPriceAlarms = async (user_id: number): Promise => { * @param user_id The id of the user that wants to update the price alarm * @param defined_price The defined price for the price alarm */ -export const updatePriceAlarm = async (alarm_id: number, user_id: number, defined_price: number): Promise => { +export const updatePriceAlarm = async (alarm_id: number, user_id: number, defined_price: number): Promise => { let conn; try { conn = await pool.getConnection(); const res = await conn.query('UPDATE price_alarms SET defined_price = ? WHERE alarm_id = ? AND user_id = ?', [defined_price, alarm_id, user_id]); - if (res.affectedRows === 1) { - return true; - } else { - return false; - } + return res.affectedRows === 1; } catch (err) { throw err; } finally { @@ -101,6 +91,4 @@ export const updatePriceAlarm = async (alarm_id: number, user_id: number, define conn.end(); } } - - return false; };