From b185e4e5e36bd0f3c630c0f5e51ffd635726a5d2 Mon Sep 17 00:00:00 2001 From: Patrick <50352812+Mueller-Patrick@users.noreply.github.com> Date: Sun, 16 May 2021 12:28:11 +0200 Subject: [PATCH 1/2] BETTERZON-93: Adding API endpoint to get managed shops (#47) --- Backend/src/models/vendors/vendors.router.ts | 24 ++++++++++++++-- Backend/src/models/vendors/vendors.service.ts | 28 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/Backend/src/models/vendors/vendors.router.ts b/Backend/src/models/vendors/vendors.router.ts index e587ca1..aca9086 100644 --- a/Backend/src/models/vendors/vendors.router.ts +++ b/Backend/src/models/vendors/vendors.router.ts @@ -6,6 +6,7 @@ import express, {Request, Response} from 'express'; import * as VendorService from './vendors.service'; import {Vendor} from './vendor.interface'; import {Vendors} from './vendors.interface'; +import * as UserService from '../users/users.service'; /** @@ -19,7 +20,7 @@ export const vendorsRouter = express.Router(); * Controller Definitions */ -// GET items/ +// GET vendors/ vendorsRouter.get('/', async (req: Request, res: Response) => { try { const vendors: Vendors = await VendorService.findAll(); @@ -31,7 +32,24 @@ vendorsRouter.get('/', async (req: Request, res: Response) => { } }); -// GET items/:id +// GET vendors/managed +vendorsRouter.get('/managed', async (req: Request, res: Response) => { + console.log('here'); + try { + // Authenticate user + const user_ip = req.connection.remoteAddress ?? ''; + const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip); + + const vendors = await VendorService.getManagedShops(user.user_id); + + res.status(200).send(vendors); + } catch (e) { + console.log('Error handling a request: ' + e.message); + res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); + } +}); + +// GET vendors/:id vendorsRouter.get('/:id', async (req: Request, res: Response) => { const id: number = parseInt(req.params.id, 10); @@ -50,7 +68,7 @@ vendorsRouter.get('/:id', async (req: Request, res: Response) => { } }); -// GET items/:name +// GET vendors/search/:term vendorsRouter.get('/search/:term', async (req: Request, res: Response) => { const term: string = req.params.term; diff --git a/Backend/src/models/vendors/vendors.service.ts b/Backend/src/models/vendors/vendors.service.ts index ca8bbee..6cd43a5 100644 --- a/Backend/src/models/vendors/vendors.service.ts +++ b/Backend/src/models/vendors/vendors.service.ts @@ -17,6 +17,7 @@ const pool = mariadb.createPool({ import {Vendor} from './vendor.interface'; import {Vendors} from './vendors.interface'; +import {User} from '../users/user.interface'; /** @@ -123,3 +124,30 @@ export const findBySearchTerm = async (term: string): Promise => { return vendorRows; }; + +/** + * Get all vendors that have the given user as admin + * @param user The user to return the managed shops for + */ +export const getManagedShops = async (user_id: number): Promise => { + let conn; + let vendorRows = []; + try { + conn = await pool.getConnection(); + const rows = await conn.query('SELECT vendor_id, name, streetname, zip_code, city, country_code, phone, website FROM vendors WHERE admin_id LIKE ?', user_id); + for (let row in rows) { + if (row !== 'meta') { + vendorRows.push(rows[row]); + } + } + + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return vendorRows; +}; From 061d1a46e00604b888878303fe1a42d0e96c5610 Mon Sep 17 00:00:00 2001 From: Patrick <50352812+Mueller-Patrick@users.noreply.github.com> Date: Sun, 16 May 2021 13:11:22 +0200 Subject: [PATCH 2/2] BETTERZON-94: Adding API endpoint to deactivate price listings as a vendor manager (#48) --- Backend/src/models/prices/prices.service.ts | 23 ++++++------- Backend/src/models/vendors/vendors.router.ts | 25 +++++++++++++- Backend/src/models/vendors/vendors.service.ts | 34 +++++++++++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/Backend/src/models/prices/prices.service.ts b/Backend/src/models/prices/prices.service.ts index 52d811f..405cfd4 100644 --- a/Backend/src/models/prices/prices.service.ts +++ b/Backend/src/models/prices/prices.service.ts @@ -31,7 +31,7 @@ export const findAll = async (): Promise => { let priceRows = []; try { conn = await pool.getConnection(); - const rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices'); + const rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE active_listing = true'); for (let row in rows) { if (row !== 'meta') { let price: Price = { @@ -72,7 +72,7 @@ export const find = async (id: number): Promise => { let price: any; try { conn = await pool.getConnection(); - const rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE price_id = ?', id); + const rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE price_id = ? AND active_listing = true', id); for (let row in rows) { if (row !== 'meta') { price = rows[row]; @@ -99,7 +99,7 @@ export const findByProduct = async (product: number): Promise => { let priceRows = []; try { conn = await pool.getConnection(); - const rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ?', product); + const rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND active_listing = true', product); for (let row in rows) { if (row !== 'meta') { priceRows.push(rows[row]); @@ -142,16 +142,16 @@ export const findByType = async (product: string, type: string): Promise 'PARTITION BY p.vendor_id ' + 'ORDER BY p.timestamp DESC) AS rk ' + 'FROM prices p ' + - 'WHERE product_id = ? AND vendor_id != 1) ' + + 'WHERE product_id = ? AND vendor_id != 1 AND active_listing = true) ' + 'SELECT s.* ' + 'FROM summary s ' + 'WHERE s.rk = 1 '), product); } else if (type === 'lowest') { // Used to get the lowest prices for this product over a period of time - rows = await conn.query('SELECT price_id, product_id, vendor_id, MIN(price_in_cents) as price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id != 1 GROUP BY DAY(timestamp) ORDER BY timestamp', product); + rows = await conn.query('SELECT price_id, product_id, vendor_id, MIN(price_in_cents) as price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id != 1 AND active_listing = true GROUP BY DAY(timestamp) ORDER BY timestamp', product); } else { // If no type is given, return all prices for this product - rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id != 1', product); + rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id != 1 AND active_listing = true', product); } for (let row in rows) { @@ -188,13 +188,13 @@ export const findByVendor = async (product: string, vendor: string, type: string let rows = []; if (type === 'newest') { // Used to get the newest price for this product and vendor - rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id = ? ORDER BY timestamp DESC LIMIT 1', [product, vendor]); + rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id = ? AND active_listing = true ORDER BY timestamp DESC LIMIT 1', [product, vendor]); } else if (type === 'lowest') { // Used to get the lowest prices for this product and vendor in all time - rows = await conn.query('SELECT price_id, product_id, vendor_id, MIN(price_in_cents) as price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id = ? LIMIT 1', [product, vendor]); + rows = await conn.query('SELECT price_id, product_id, vendor_id, MIN(price_in_cents) as price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id = ? AND active_listing = true LIMIT 1', [product, vendor]); } else { // If no type is given, return all prices for this product and vendor - rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id = ?', [product, vendor]); + rows = await conn.query('SELECT price_id, product_id, vendor_id, price_in_cents, timestamp FROM prices WHERE product_id = ? AND vendor_id = ? AND active_listing = true', [product, vendor]); } for (let row in rows) { @@ -237,7 +237,7 @@ export const getBestDeals = async (amount: number): Promise => { ' ROW_NUMBER() OVER(\n' + ' PARTITION BY p.product_id, p.vendor_id\n' + ' ORDER BY p.timestamp DESC) AS rk\n' + - ' FROM prices p)\n' + + ' FROM prices p WHERE active_listing = true)\n' + 'SELECT s.*\n' + 'FROM summary s\n' + 'WHERE s.rk = 1'); @@ -298,7 +298,6 @@ export const getBestDeals = async (amount: number): Promise => { let maxAmt = Math.min(amount, deals.length); for (let dealIndex = 0; dealIndex < maxAmt; dealIndex++) { - //console.log(deals[dealIndex]); priceRows.push(deals[dealIndex] as Price); } @@ -338,7 +337,7 @@ export const findListByProducts = async (productIds: [number]): Promise ' ORDER BY p.timestamp DESC) AS rk\n' + ' FROM prices p' + ' WHERE p.product_id IN (?)' + - ' AND p.vendor_id != 1)\n' + + ' AND p.vendor_id != 1 AND active_listing = true)\n' + 'SELECT s.*\n' + 'FROM summary s\n' + 'WHERE s.rk = 1', [productIds]); diff --git a/Backend/src/models/vendors/vendors.router.ts b/Backend/src/models/vendors/vendors.router.ts index aca9086..7ca180d 100644 --- a/Backend/src/models/vendors/vendors.router.ts +++ b/Backend/src/models/vendors/vendors.router.ts @@ -34,7 +34,6 @@ vendorsRouter.get('/', async (req: Request, res: Response) => { // GET vendors/managed vendorsRouter.get('/managed', async (req: Request, res: Response) => { - console.log('here'); try { // Authenticate user const user_ip = req.connection.remoteAddress ?? ''; @@ -86,3 +85,27 @@ vendorsRouter.get('/search/:term', async (req: Request, res: Response) => { res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); } }); + +// PUT /manage/deactivatelisting +vendorsRouter.put('/manage/deactivatelisting', 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 required parameters + const vendor_id = req.body.vendor_id; + const product_id = req.body.product_id; + + const success = await VendorService.deactivateListing(user.user_id, vendor_id, product_id); + + if(success) { + res.sendStatus(200); + } else { + res.sendStatus(500); + } + } 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/vendors/vendors.service.ts b/Backend/src/models/vendors/vendors.service.ts index 6cd43a5..1dc2220 100644 --- a/Backend/src/models/vendors/vendors.service.ts +++ b/Backend/src/models/vendors/vendors.service.ts @@ -151,3 +151,37 @@ export const getManagedShops = async (user_id: number): Promise => { return vendorRows; }; + +/** + * Deactivates a product listing for a specific vendor + * @param user_id The user id of the issuing user + * @param vendor_id The vendor id of the vendor to deactivate the listing for + * @param product_id The product id of the product to deactivate the listing for + */ +export const deactivateListing = async (user_id: number, vendor_id: number, product_id: number): Promise => { + let conn; + try { + conn = await pool.getConnection(); + + // Check if the user is authorized to manage the requested vendor + const user_vendor_rows = await conn.query('SELECT vendor_id FROM vendors WHERE vendor_id = ? AND admin_id = ?', [vendor_id, user_id]); + if (user_vendor_rows.length !== 1) { + return false; + } + + const status = await conn.query('UPDATE prices SET active_listing = false WHERE vendor_id = ? and product_id = ?', [vendor_id, product_id]); + + if(status.affectedRows > 0){ + return true; + } + return false; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return false; +};