From 1581184b5742abd39228738e6c405e2772a7cfb0 Mon Sep 17 00:00:00 2001 From: Patrick <50352812+Mueller-Patrick@users.noreply.github.com> Date: Wed, 19 May 2021 09:08:52 +0200 Subject: [PATCH] BETTERZON-96: Adding API endpoint for delisting a whole vendor (#54) --- Backend/src/models/prices/prices.service.ts | 30 ++++++------ Backend/src/models/vendors/vendors.router.ts | 46 +++++++++++++++++++ Backend/src/models/vendors/vendors.service.ts | 38 +++++++++++++-- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/Backend/src/models/prices/prices.service.ts b/Backend/src/models/prices/prices.service.ts index 0a5ab2a..acc6793 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 WHERE active_listing = true'); + const rows = await conn.query('SELECT price_id, product_id, v.vendor_id, price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE active_listing = true AND v.isActive = 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 = ? AND active_listing = true', id); + const rows = await conn.query('SELECT price_id, product_id, p.vendor_id, price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE price_id = ? AND active_listing = true AND v.isActive = 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 = ? AND active_listing = true', product); + const rows = await conn.query('SELECT price_id, product_id, p.vendor_id, price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE product_id = ? AND active_listing = true AND v.isActive = true', product); for (let row in rows) { if (row !== 'meta') { priceRows.push(rows[row]); @@ -142,16 +142,17 @@ 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 AND active_listing = true) ' + + 'LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id ' + + 'WHERE product_id = ? AND p.vendor_id != 1 AND active_listing = true AND v.isActive = 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 AND active_listing = true GROUP BY DAY(timestamp) ORDER BY timestamp', product); + rows = await conn.query('SELECT price_id, product_id, p.vendor_id, MIN(price_in_cents) as price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE product_id = ? AND v.vendor_id != 1 AND active_listing = true AND v.isActive = 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 AND active_listing = true', product); + rows = await conn.query('SELECT price_id, product_id, p.vendor_id, price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE product_id = ? AND p.vendor_id != 1 AND active_listing = true AND v.isActive = true', product); } for (let row in rows) { @@ -188,13 +189,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 = ? AND active_listing = true ORDER BY timestamp DESC LIMIT 1', [product, vendor]); + rows = await conn.query('SELECT price_id, product_id, p.vendor_id, price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE product_id = ? AND p.vendor_id = ? AND active_listing = true AND v.isActive = 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 = ? AND active_listing = true LIMIT 1', [product, vendor]); + rows = await conn.query('SELECT price_id, product_id, p.vendor_id, MIN(price_in_cents) as price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE product_id = ? AND p.vendor_id = ? AND active_listing = true AND v.isActive = 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 = ? AND active_listing = true', [product, vendor]); + rows = await conn.query('SELECT price_id, product_id, p.vendor_id, price_in_cents, timestamp FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE product_id = ? AND p.vendor_id = ? AND active_listing = true AND v.isActive = true', [product, vendor]); } for (let row in rows) { @@ -237,7 +238,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 WHERE active_listing = true)\n' + + ' FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id WHERE active_listing = true AND v.isActive = true)\n' + 'SELECT s.*\n' + 'FROM summary s\n' + 'WHERE s.rk = 1'); @@ -300,7 +301,6 @@ export const getBestDeals = async (amount: number): Promise => { for (let dealIndex = 0; dealIndex < maxAmt; dealIndex++) { priceRows.push(deals[dealIndex] as Price); } - } catch (err) { console.log(err); throw err; @@ -315,7 +315,7 @@ export const getBestDeals = async (amount: number): Promise => { /** * Fetches and returns the lowest, latest, non-amazon price for each given product - * @param ids the ids of the products + * @param productIds the ids of the products */ export const findListByProducts = async (productIds: [number]): Promise => { let conn; @@ -335,8 +335,8 @@ export const findListByProducts = async (productIds: [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' + - ' WHERE p.product_id IN (?)' + + ' FROM prices p LEFT OUTER JOIN vendors v ON v.vendor_id = p.vendor_id ' + + ' WHERE p.product_id IN (?) AND v.isActive = true' + ' AND p.vendor_id != 1 AND active_listing = true)\n' + 'SELECT s.*\n' + 'FROM summary s\n' + @@ -365,7 +365,6 @@ export const findListByProducts = async (productIds: [number]): Promise priceRows.push(pricesForProd[0]); } }); - } catch (err) { throw err; } finally { @@ -393,7 +392,6 @@ export const createPriceEntry = async (user_id: number, vendor_id: number, produ // If there are more / less than 1 affected rows, return false return res.affectedRows === 1; - } catch (err) { throw err; } finally { diff --git a/Backend/src/models/vendors/vendors.router.ts b/Backend/src/models/vendors/vendors.router.ts index 7440f4f..89d02fd 100644 --- a/Backend/src/models/vendors/vendors.router.ts +++ b/Backend/src/models/vendors/vendors.router.ts @@ -109,3 +109,49 @@ vendorsRouter.put('/manage/deactivatelisting', async (req: Request, res: Respons res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'})); } }); + +// PUT /manage/shop/deactivate/:id +vendorsRouter.put('/manage/shop/deactivate/: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 required parameters + const vendor_id = parseInt(req.params.id, 10); + + const success = await VendorService.setShopStatus(user.user_id, vendor_id, false); + + 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.'})); + } +}); + +// PUT /manage/shop/activate/:id +vendorsRouter.put('/manage/shop/activate/: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 required parameters + const vendor_id = parseInt(req.params.id, 10); + + const success = await VendorService.setShopStatus(user.user_id, vendor_id, true); + + 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 39fee7d..2439093 100644 --- a/Backend/src/models/vendors/vendors.service.ts +++ b/Backend/src/models/vendors/vendors.service.ts @@ -32,7 +32,7 @@ export const findAll = async (): Promise => { 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'); + const rows = await conn.query('SELECT vendor_id, name, streetname, zip_code, city, country_code, phone, website FROM vendors WHERE isActive = true'); for (let row in rows) { if (row !== 'meta') { let vendor: Vendor = { @@ -79,7 +79,7 @@ export const find = async (id: number): Promise => { let vendor: any; 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 vendor_id = ?', id); + const rows = await conn.query('SELECT vendor_id, name, streetname, zip_code, city, country_code, phone, website FROM vendors WHERE vendor_id = ? AND isActive = true', id); for (let row in rows) { if (row !== 'meta') { vendor = rows[row]; @@ -107,7 +107,7 @@ export const findBySearchTerm = async (term: string): Promise => { try { conn = await pool.getConnection(); term = '%' + term + '%'; - const rows = await conn.query('SELECT vendor_id, name, streetname, zip_code, city, country_code, phone, website FROM vendors WHERE name LIKE ?', term); + const rows = await conn.query('SELECT vendor_id, name, streetname, zip_code, city, country_code, phone, website FROM vendors WHERE name LIKE ? AND isActive = true', term); for (let row in rows) { if (row !== 'meta') { vendorRows.push(rows[row]); @@ -182,3 +182,35 @@ export const deactivateListing = async (user_id: number, vendor_id: number, prod return false; }; + +/** + * Set the specified shop to either active or not active + * @param user_id The user id of the issuing user + * @param vendor_id The vendor id of the shop to update + * @param isActive The new active state + */ +export const setShopStatus = async (user_id: number, vendor_id: number, isActive: boolean): 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; + } + + // Update the vendor state + const status = await conn.query('UPDATE vendors SET isActive = ? WHERE vendor_id = ?', [isActive, vendor_id]); + + return status.affectedRows > 0; + } catch (err) { + throw err; + } finally { + if (conn) { + conn.end(); + } + } + + return false; +};