mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2024-11-12 17:43:57 +00:00
Merge remote-tracking branch 'origin/develop' into BETTERZON-140
This commit is contained in:
commit
012de346e8
|
@ -25,8 +25,8 @@ crawlingstatusRouter.get('/', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Authenticate user
|
||||
const user_ip = req.connection.remoteAddress ?? '';
|
||||
const session_id = req.body.session_id;
|
||||
const session_key = req.body.session_key;
|
||||
const session_id = (req.query.session_id ?? '').toString();
|
||||
const session_key = (req.query.session_key ?? '').toString();
|
||||
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||
|
||||
if (!user.is_admin) {
|
||||
|
|
|
@ -24,8 +24,8 @@ favoriteshopsRouter.get('/', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Authenticate user
|
||||
const user_ip = req.connection.remoteAddress ?? '';
|
||||
const session_id = req.params.session_id;
|
||||
const session_key = req.params.session_key;
|
||||
const session_id = (req.query.session_id ?? '').toString();
|
||||
const session_key = (req.query.session_key ?? '').toString();
|
||||
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||
|
||||
const priceAlarms = await FavoriteShopsService.getFavoriteShops(user.user_id);
|
||||
|
@ -76,8 +76,8 @@ favoriteshopsRouter.delete('/:id', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Authenticate user
|
||||
const user_ip = req.connection.remoteAddress ?? '';
|
||||
const session_id = req.params.session_id;
|
||||
const session_key = req.params.session_key;
|
||||
const session_id = (req.query.session_id ?? '').toString();
|
||||
const session_key = (req.query.session_key ?? '').toString();
|
||||
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||
|
||||
// Get info for price alarm creation
|
||||
|
|
|
@ -24,8 +24,8 @@ pricealarmsRouter.get('/', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Authenticate user
|
||||
const user_ip = req.connection.remoteAddress ?? '';
|
||||
const session_id = req.params.session_id;
|
||||
const session_key = req.params.session_key;
|
||||
const session_id = (req.query.session_id ?? '').toString();
|
||||
const session_key = (req.query.session_key ?? '').toString();
|
||||
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||
|
||||
const priceAlarms = await PriceAlarmsService.getPriceAlarms(user.user_id);
|
||||
|
@ -106,3 +106,29 @@ pricealarmsRouter.put('/', async (req: Request, res: Response) => {
|
|||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE pricealarms/:id
|
||||
pricealarmsRouter.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
// Authenticate user
|
||||
const user_ip = req.connection.remoteAddress ?? '';
|
||||
const session_id = (req.query.session_id ?? '').toString();
|
||||
const session_key = (req.query.session_key ?? '').toString();
|
||||
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||
|
||||
const id: number = parseInt(req.params.id, 10);
|
||||
|
||||
const success = await PriceAlarmsService.deletePriceAlarm(id, user.user_id);
|
||||
|
||||
if (success) {
|
||||
res.status(200).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.'}));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -92,3 +92,24 @@ export const updatePriceAlarm = async (alarm_id: number, user_id: number, define
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the given price alarm
|
||||
* @param alarm_id The id of the price alarm to update
|
||||
* @param user_id The id of the user that wants to update the price alarm
|
||||
*/
|
||||
export const deletePriceAlarm = async (alarm_id: number, user_id: number): Promise<boolean> => {
|
||||
let conn;
|
||||
try {
|
||||
conn = await pool.getConnection();
|
||||
const res = await conn.query('DELETE FROM price_alarms WHERE alarm_id = ? AND user_id = ?', [alarm_id, user_id]);
|
||||
|
||||
return res.affectedRows === 1;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
if (conn) {
|
||||
conn.end();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -282,11 +282,11 @@ export const getBestDeals = async (amount: number): Promise<Prices> => {
|
|||
'price_in_cents': lowestPrice.price_in_cents,
|
||||
'timestamp': lowestPrice.timestamp,
|
||||
'amazonDifference': (amazonPrice.price_in_cents - lowestPrice.price_in_cents),
|
||||
'amazonDifferencePercent': ((1 - (lowestPrice.price_in_cents / amazonPrice.price_in_cents)) * 100),
|
||||
'amazonDifferencePercent': ((amazonPrice.price_in_cents / lowestPrice.price_in_cents) * 100),
|
||||
};
|
||||
|
||||
// Push only deals were the amazon price is actually higher
|
||||
if (deal.amazonDifferencePercent > 0) {
|
||||
if (deal.amazonDifferencePercent > 0 && deal.amazonDifference > 0) {
|
||||
deals.push(deal as Deal);
|
||||
}
|
||||
}
|
||||
|
|
4
Backend/src/models/vendors/vendors.router.ts
vendored
4
Backend/src/models/vendors/vendors.router.ts
vendored
|
@ -37,8 +37,8 @@ vendorsRouter.get('/managed', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Authenticate user
|
||||
const user_ip = req.connection.remoteAddress ?? '';
|
||||
const session_id = req.params.session_id;
|
||||
const session_key = req.params.session_key;
|
||||
const session_id = (req.query.session_id ?? '').toString();
|
||||
const session_key = (req.query.session_key ?? '').toString();
|
||||
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||
|
||||
const vendors = await VendorService.getManagedShops(user.user_id);
|
||||
|
|
|
@ -8,14 +8,16 @@ import stepdefs.Preconditions;
|
|||
|
||||
@RunWith(Cucumber.class)
|
||||
@CucumberOptions(
|
||||
features = {"src/test/resource/searchProduct.feature",
|
||||
"src/test/resource/priceAlarms.feature"}
|
||||
features = {"src/test/resource/searchProduct.feature",
|
||||
"src/test/resource/priceAlarms.feature",
|
||||
"src/test/resource/favoriteShopList.feature",
|
||||
"src/test/resource/manageVendor.feature"}
|
||||
)
|
||||
|
||||
public class RunTest {
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
Preconditions.driver= new FirefoxDriver();
|
||||
Preconditions.driver = new FirefoxDriver();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
27
CucumberTests/src/test/java/stepdefs/FavoriteShopList.java
Normal file
27
CucumberTests/src/test/java/stepdefs/FavoriteShopList.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package stepdefs;
|
||||
|
||||
import io.cucumber.java.en.Given;
|
||||
import io.cucumber.java.en.Then;
|
||||
import io.cucumber.java.en.When;
|
||||
|
||||
public class FavoriteShopList {
|
||||
@Given("^the user has at least (\\d+) favorite shop$")
|
||||
public void the_user_has_at_least_favorite_shop(int arg1) throws Exception {
|
||||
}
|
||||
|
||||
@When("^the user clicks on favorite shops$")
|
||||
public void the_user_clicks_on_favorite_shops() throws Exception {
|
||||
}
|
||||
|
||||
@Then("^he should see his favorite shops list$")
|
||||
public void he_Should_see_his_favorite_shops_list() throws Exception {
|
||||
}
|
||||
|
||||
@When("^he clicks on delete a favorite shop entry$")
|
||||
public void he_clicks_on_delete_a_favorite_shop_enty() throws Exception {
|
||||
}
|
||||
|
||||
@Then("^the favorite shop entry should be deleted$")
|
||||
public void the_favorite_shop_entry_should_be_deleted() throws Exception {
|
||||
}
|
||||
}
|
31
CucumberTests/src/test/java/stepdefs/ManageVendor.java
Normal file
31
CucumberTests/src/test/java/stepdefs/ManageVendor.java
Normal file
|
@ -0,0 +1,31 @@
|
|||
package stepdefs;
|
||||
|
||||
import io.cucumber.java.en.Given;
|
||||
import io.cucumber.java.en.Then;
|
||||
import io.cucumber.java.en.When;
|
||||
|
||||
public class ManageVendor {
|
||||
@Given("^the user is logged in as vendor manager$")
|
||||
public void the_user_is_logged_in_as_vendor_manager() throws Exception {
|
||||
}
|
||||
|
||||
@When("^the user opens the shop managing page$")
|
||||
public void the_user_opens_the_shop_managing_page() throws Exception {
|
||||
}
|
||||
|
||||
@When("^the user clicks on deactivate a listing$")
|
||||
public void the_user_clicks_on_deactivate_a_listing() throws Exception {
|
||||
}
|
||||
|
||||
@Then("^the listing should be deactivated$")
|
||||
public void the_listing_should_be_deactivated() throws Exception {
|
||||
}
|
||||
|
||||
@When("^the user clicks on deactivate the shop$")
|
||||
public void the_user_clicks_on_deactivate_the_shop() throws Exception {
|
||||
}
|
||||
|
||||
@Then("^the shop and all related listings should be deactivated$")
|
||||
public void the_shop_and_all_related_listings_should_be_deactivated() throws Exception {
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ public class SearchProduct {
|
|||
//throw new PendingException();
|
||||
Preconditions.driver.get("https://betterzon.xyz");
|
||||
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".logo")));
|
||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
||||
}
|
||||
|
||||
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
||||
|
@ -25,7 +25,7 @@ public class SearchProduct {
|
|||
searchField.sendKeys(searchTerm);
|
||||
searchField.sendKeys(Keys.ENTER);
|
||||
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".logo")));
|
||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
||||
}
|
||||
|
||||
@Then("^the user should see the error page \"([^\"]*)\"$")
|
||||
|
|
20
CucumberTests/src/test/resource/favoriteShopList.feature
Normal file
20
CucumberTests/src/test/resource/favoriteShopList.feature
Normal file
|
@ -0,0 +1,20 @@
|
|||
Feature: Favorite Shop List
|
||||
|
||||
Scenario: Access Favorite Shop List
|
||||
Given the user is on the landing page
|
||||
And the user is logged in
|
||||
And the user has at least 1 favorite shop
|
||||
When the user clicks on the profile icon
|
||||
Then the profile details popup should open
|
||||
When the user clicks on favorite shops
|
||||
Then he should see his favorite shops list
|
||||
|
||||
Scenario: Remove Favorite Shop Entry
|
||||
Given the user is on the landing page
|
||||
And the user is logged in
|
||||
And the user has at least 1 favorite shop
|
||||
When the user clicks on the profile icon
|
||||
Then the profile details popup should open
|
||||
When the user clicks on favorite shops
|
||||
And he clicks on delete a favorite shop entry
|
||||
Then the favorite shop entry should be deleted
|
15
CucumberTests/src/test/resource/manageVendor.feature
Normal file
15
CucumberTests/src/test/resource/manageVendor.feature
Normal file
|
@ -0,0 +1,15 @@
|
|||
Feature: Manage Vendor Shop
|
||||
|
||||
Scenario: Deactivate Product Listing
|
||||
Given the user is on the landing page
|
||||
And the user is logged in as vendor manager
|
||||
When the user opens the shop managing page
|
||||
And the user clicks on deactivate a listing
|
||||
Then the listing should be deactivated
|
||||
|
||||
Scenario: Deactivate Shop Completely
|
||||
Given the user is on the landing page
|
||||
And the user is logged in as vendor manager
|
||||
When the user opens the shop managing page
|
||||
And the user clicks on deactivate the shop
|
||||
Then the shop and all related listings should be deactivated
|
|
@ -1,3 +1,3 @@
|
|||
<div class="copyright py-4 text-center text-white">
|
||||
<div class="container"><small>Copyright © Your Website 2021</small></div>
|
||||
<div class="container"><small>Copyright © Betterzon 2021</small></div>
|
||||
</div>
|
||||
|
|
|
@ -445,6 +445,25 @@ export class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given price alarm
|
||||
* @param alarmId the price alarm to delete
|
||||
* @return Observable<any> The observable response of the api
|
||||
*/
|
||||
deletePriceAlarm(alarmId: number): Observable<any> {
|
||||
try {
|
||||
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||
|
||||
let params = new HttpParams();
|
||||
params = params.append('session_id', sessionInfo.session_id);
|
||||
params = params.append('session_key', sessionInfo.session_key);
|
||||
|
||||
return this.http.delete((this.apiUrl + '/pricealarms/' + alarmId), {params});
|
||||
} catch (exception) {
|
||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* __ __
|
||||
/ / / /_______ __________
|
||||
|
|
|
@ -1 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2020-12-03T09:39:52.243Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" etag="RUzsjnuqX_zRTCHSTruo" version="13.10.6" type="github"><diagram id="QFWcWedTnleHV76omDGD" name="Page-1">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</diagram></mxfile>
|
||||
<mxfile host="Electron" modified="2021-06-15T10:10:23.336Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="xPXaWnl_FRrq1LtBykUU" version="13.4.5" type="device"><diagram id="QFWcWedTnleHV76omDGD" name="Page-1">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</diagram></mxfile>
|
Binary file not shown.
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 85 KiB |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user