Merge branch '#37-eBay-Product-API' into 'dev'
eBay Product API See merge request team1/sandbox2!3
This commit is contained in:
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@@ -1,3 +1,8 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "automatic"
|
||||
}
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"sonarlint.connectedMode.project": {
|
||||
"connectionId": "LocalSonarQube",
|
||||
"projectKey": "EasyDrop"
|
||||
},
|
||||
"java.debug.settings.onBuildFailureProceed": true
|
||||
}
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,10 +1,34 @@
|
||||
# Changelog
|
||||
|
||||
## 0.2.0-SNAPSHOT
|
||||
|
||||
### Added
|
||||
|
||||
- `EbayItemDataSource` (#37)
|
||||
- `Product` class
|
||||
- Mapping between Product and its corresponding DTO
|
||||
- Dependencies: Lombok for easier notation, JUnit 5 Params for repetitive tests
|
||||
- Product Validation
|
||||
- Product Retrieval
|
||||
- Tests
|
||||
|
||||
### Changed
|
||||
|
||||
- `AbstractDataSource` between DataSource interface and implementations to minimize code duplication
|
||||
- Affects AmazonProductDataSource and related tests (~ #39)
|
||||
- Testing resources moved to more intuitive location
|
||||
- Demo `Main` method now uses proper Product retrieval
|
||||
|
||||
### Fixed
|
||||
|
||||
- Logback configuration is now linked correctly
|
||||
- Config now properly adheres to singleton pattern
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Added
|
||||
|
||||
- (DataSource) → AmazonProductDataSource as first external data source (#39)
|
||||
- ProductDTO as data holder structure for data from external sources (~ #39)
|
||||
- (DataSource) → `AmazonProductDataSource` as first external data source (#39)
|
||||
- `ProductDTO` as data holder structure for data from external sources (~ #39)
|
||||
- Config for credential, base URLs etc. (~ #39)
|
||||
- FormattingUtil for formatting price strings (~ #39)
|
||||
|
||||
133
SampleResponses/eBayProductAPISample.json
Normal file
133
SampleResponses/eBayProductAPISample.json
Normal file
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"href": "https://api.ebay.com/buy/browse/v1/item_summary/search?q=drone&limit=1&offset=0",
|
||||
"total": 260202,
|
||||
"next": "https://api.ebay.com/buy/browse/v1/item_summary/search?q=drone&limit=1&offset=1",
|
||||
"limit": 3,
|
||||
"offset": 0,
|
||||
"itemSummaries": [
|
||||
{
|
||||
"itemId": "v1|1**********1|0",
|
||||
"title": "Syma X5SW-V3 Wifi FPV RC Drone Quadcopter 2.4Ghz 6-Axis Gyro with Headless Mode",
|
||||
"leafCategoryIds": ["179697", "182186"],
|
||||
"categories": [
|
||||
{
|
||||
"categoryId": "179697",
|
||||
"categoryName": "Camera Drones"
|
||||
},
|
||||
{
|
||||
"categoryId": "182186",
|
||||
"categoryName": "Other RC Model Vehicles & Kits"
|
||||
},
|
||||
{
|
||||
"categoryId": "2562",
|
||||
"categoryName": "Radio Control & Control Line"
|
||||
},
|
||||
{
|
||||
"categoryId": "220",
|
||||
"categoryName": "Toys & Hobbies"
|
||||
},
|
||||
{
|
||||
"categoryId": "625",
|
||||
"categoryName": "Cameras & Photo"
|
||||
},
|
||||
{
|
||||
"categoryId": "182181",
|
||||
"categoryName": "RC Model Vehicles & Kits"
|
||||
}
|
||||
],
|
||||
"image": {
|
||||
"imageUrl": "https://i.ebayimg.com/thumbs/images/g/n**************a/s-***5.jpg"
|
||||
},
|
||||
"price": {
|
||||
"value": "59.99",
|
||||
"currency": "USD"
|
||||
},
|
||||
"itemHref": "https://api.ebay.com/buy/browse/v1/item/v1******************0",
|
||||
"seller": {
|
||||
"username": "m********e",
|
||||
"feedbackPercentage": "98.6",
|
||||
"feedbackScore": 130000
|
||||
},
|
||||
"marketingPrice": {
|
||||
"originalPrice": {
|
||||
"value": "74.99",
|
||||
"currency": "USD"
|
||||
},
|
||||
"discountPercentage": "20",
|
||||
"discountAmount": {
|
||||
"value": "15.00",
|
||||
"currency": "USD"
|
||||
},
|
||||
"priceTreatment": "LIST_PRICE"
|
||||
},
|
||||
"condition": "New",
|
||||
"conditionId": "1000",
|
||||
"thumbnailImages": [
|
||||
{
|
||||
"imageUrl": "https://i.ebayimg.com/images/g/n**************a/s-l***0.jpg"
|
||||
}
|
||||
],
|
||||
"shippingOptions": [
|
||||
{
|
||||
"shippingCostType": "FIXED",
|
||||
"shippingCost": {
|
||||
"value": "0.00",
|
||||
"currency": "USD"
|
||||
},
|
||||
"minEstimatedDeliveryDate": "2022-11-19T08:00:00.000Z",
|
||||
"maxEstimatedDeliveryDate": "2022-11-21T08:00:00.000Z",
|
||||
"guaranteedDelivery": true
|
||||
}
|
||||
],
|
||||
"buyingOptions": ["FIXED_PRICE", "BEST_OFFER"],
|
||||
"itemAffiliateWebUrl": "https://www.ebay.com/itm/1**********1?hash=i************d*9",
|
||||
"itemWebUrl": "https://www.ebay.com/itm/1**********1?hash=i************d*p",
|
||||
"itemLocation": {
|
||||
"postalCode": "0****",
|
||||
"country": "US"
|
||||
},
|
||||
"additionalImages": [
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_2_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_3_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_4_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_5_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_6_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_7_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_8_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_9_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_10_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_11_0_1/225x225.jpg"
|
||||
},
|
||||
{
|
||||
"imageUrl": "https://origin-galleryplus.ebayimg.com/ws/web/1**********1_12_0_1/225x225.jpg"
|
||||
}
|
||||
],
|
||||
"adultOnly": false,
|
||||
"legacyItemId": "1**********1",
|
||||
"availableCoupons": false,
|
||||
"itemCreationDate": "2022-12-25T07:14:44.000Z",
|
||||
"topRatedBuyingExperience": true,
|
||||
"priorityListing": true,
|
||||
"listingMarketplaceId": "EBAY_US"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
Script/SonarQube_Local.sh
Normal file
5
Script/SonarQube_Local.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
mvn clean verify sonar:sonar -Pcoverage \
|
||||
-Dsonar.projectKey=EasyDrop \
|
||||
-Dsonar.projectName='EasyDrop' \
|
||||
-Dsonar.host.url=http://localhost:9000 \
|
||||
-Dsonar.token=sqp_82d35689c620c15fd1064549375e17a2a5b0b931
|
||||
@@ -1,3 +1,5 @@
|
||||
# Amazon Credentials
|
||||
AMAZON_API_URL=
|
||||
AMAZON_API_KEY=
|
||||
AMAZON_API_KEY=
|
||||
EBAY_API_URL=
|
||||
EBAY_API_KEY=
|
||||
2
lombok.config
Normal file
2
lombok.config
Normal file
@@ -0,0 +1,2 @@
|
||||
config.stopBubbling = true
|
||||
lombok.addLombokGeneratedAnnotation = true
|
||||
19
pom.xml
19
pom.xml
@@ -5,7 +5,7 @@
|
||||
<groupId>de.rwu</groupId>
|
||||
<artifactId>easydrop</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1.0</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<name>EasyDrop</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
@@ -16,16 +16,29 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.28</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.8.0</version>
|
||||
<version>5.9.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.8.0</version>
|
||||
<version>5.9.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>5.9.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import javax.naming.ConfigurationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.rwu.easydrop.api.client.AmazonProductDataSource;
|
||||
import de.rwu.easydrop.api.client.DataSourceFactory;
|
||||
import de.rwu.easydrop.service.retriever.ProductRetriever;
|
||||
import de.rwu.easydrop.util.Config;
|
||||
|
||||
/**
|
||||
@@ -33,17 +34,12 @@ public final class Main {
|
||||
*/
|
||||
public static void main(final String[] args) throws ConfigurationException {
|
||||
Config config = Config.getInstance();
|
||||
config.loadConfig();
|
||||
String amznBaseUrl = config.getProperty("AMAZON_API_URL");
|
||||
String amznApiKey = config.getProperty("AMAZON_API_KEY");
|
||||
String testProduct = null;
|
||||
DataSourceFactory dataSourceFactory = new DataSourceFactory(config);
|
||||
ProductRetriever retriever = new ProductRetriever(dataSourceFactory);
|
||||
|
||||
AmazonProductDataSource amznSrc = new AmazonProductDataSource(amznBaseUrl, amznApiKey);
|
||||
try {
|
||||
testProduct = amznSrc.getProductDTOById("B096Y2TYKV").toString();
|
||||
LOGGER.info(testProduct);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Something went wrong :(", e);
|
||||
}
|
||||
String amznProduct = retriever.getProductFromAmazon("B096Y2TYKV").toString();
|
||||
LOGGER.info(amznProduct);
|
||||
String ebayProduct = retriever.getProductFromEbay("Gigabyte GeForce RTX 3060").toString();
|
||||
LOGGER.info(ebayProduct);
|
||||
}
|
||||
}
|
||||
|
||||
100
src/main/java/de/rwu/easydrop/api/client/AbstractDataSource.java
Normal file
100
src/main/java/de/rwu/easydrop/api/client/AbstractDataSource.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.exception.DataSourceException;
|
||||
import de.rwu.easydrop.util.FormattingUtil;
|
||||
|
||||
/**
|
||||
* Helper class for shared data and functions between data sources.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public abstract class AbstractDataSource implements DataSource {
|
||||
|
||||
/**
|
||||
* Returns the data origin for the current source.
|
||||
*
|
||||
* @return Data source name
|
||||
*/
|
||||
protected abstract String getDataOrigin();
|
||||
|
||||
/**
|
||||
* Returns the data source's API key.
|
||||
*
|
||||
* @return Data source API key
|
||||
*/
|
||||
protected abstract String getApiKey();
|
||||
|
||||
/**
|
||||
* Enriches a ProductDTO with API-gathered data.
|
||||
*
|
||||
* @param product Unfinished ProductDTO
|
||||
* @param json Product data
|
||||
* @return Finished ProductDTO
|
||||
*/
|
||||
protected abstract ProductDTO buildProductDTO(ProductDTO product, String json);
|
||||
|
||||
/**
|
||||
* Overridable standard implementation.
|
||||
*/
|
||||
@Override
|
||||
public ProductDTO getProductDTOById(final String productIdentifier)
|
||||
throws IllegalArgumentException {
|
||||
StringBuilder response = new StringBuilder();
|
||||
String dataOrigin = getDataOrigin();
|
||||
String apiKey = getApiKey();
|
||||
ProductDTO product = new ProductDTO(productIdentifier, dataOrigin);
|
||||
|
||||
try {
|
||||
String urlReadyIdentifier = FormattingUtil.urlEncode(productIdentifier);
|
||||
URL apiUrl = createApiUrl(urlReadyIdentifier);
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Credential", apiKey);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
BufferedReader reader;
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
} else {
|
||||
throw new DataSourceException(
|
||||
"Nothing found: "
|
||||
+ dataOrigin
|
||||
+ " API responded with error code "
|
||||
+ responseCode);
|
||||
}
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
reader.close();
|
||||
|
||||
buildProductDTO(product, response.toString());
|
||||
} catch (IOException e) {
|
||||
throw new DataSourceException(
|
||||
"Couldn't fulfill "
|
||||
+ dataOrigin
|
||||
+ " API request");
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an URL object to connect to the API with.
|
||||
*
|
||||
* @param productIdentifier Product identifier
|
||||
* @return URL object
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
protected abstract URL createApiUrl(String productIdentifier) throws MalformedURLException;
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
@@ -18,7 +14,7 @@ import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class AmazonProductDataSource implements DataSource {
|
||||
public final class AmazonProductDataSource extends AbstractDataSource {
|
||||
/**
|
||||
* Name of this data source.
|
||||
*/
|
||||
@@ -28,7 +24,7 @@ public final class AmazonProductDataSource implements DataSource {
|
||||
*/
|
||||
private String baseUrl;
|
||||
/**
|
||||
* Credential key to authorize acccess.
|
||||
* Credential key to authorize access.
|
||||
*/
|
||||
private String apiKey;
|
||||
/**
|
||||
@@ -52,48 +48,7 @@ public final class AmazonProductDataSource implements DataSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProductDTO getProductDTOById(final String productId) throws IllegalArgumentException {
|
||||
StringBuilder response = new StringBuilder();
|
||||
ProductDTO product = new ProductDTO(productId, DATA_ORIGIN);
|
||||
|
||||
try {
|
||||
URL apiUrl = createApiUrl(productId);
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Credential", apiKey);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
BufferedReader reader;
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Nothing found: Amazon API responded with error code " + responseCode);
|
||||
}
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
reader.close();
|
||||
|
||||
buildProductDTO(product, response.toString());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Couldn't fulfill Amazon API request");
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriches a ProductDTO with API-gathered data.
|
||||
*
|
||||
* @param product Unfinished ProductDTO
|
||||
* @param json Product data
|
||||
* @return Finished ProductDTO
|
||||
*/
|
||||
public ProductDTO buildProductDTO(final ProductDTO product, final String json) {
|
||||
protected ProductDTO buildProductDTO(final ProductDTO product, final String json) {
|
||||
String root = "$.featuredOffer.";
|
||||
ReadContext ctx = JsonPath.parse(json);
|
||||
|
||||
@@ -112,8 +67,11 @@ public final class AmazonProductDataSource implements DataSource {
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param productId ASIN
|
||||
*/
|
||||
@Override
|
||||
public URL createApiUrl(final String productId) throws MalformedURLException {
|
||||
protected URL createApiUrl(final String productId) throws MalformedURLException {
|
||||
return new URL(baseUrl
|
||||
+ "/products/2020-08-26/products/"
|
||||
+ productId
|
||||
@@ -122,4 +80,14 @@ public final class AmazonProductDataSource implements DataSource {
|
||||
+ "&locale="
|
||||
+ LOCALE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDataOrigin() {
|
||||
return DATA_ORIGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getApiKey() {
|
||||
return this.apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
|
||||
/**
|
||||
@@ -12,17 +9,8 @@ public interface DataSource {
|
||||
/**
|
||||
* Retrieves product info from the data source.
|
||||
*
|
||||
* @param productId ASIN
|
||||
* @param productIdentifier Product identifier
|
||||
* @return ProductDTO
|
||||
*/
|
||||
ProductDTO getProductDTOById(String productId);
|
||||
|
||||
/**
|
||||
* Creates an URL object to connect to the API with.
|
||||
*
|
||||
* @param productId ASIN
|
||||
* @return URL object
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
URL createApiUrl(String productId) throws MalformedURLException;
|
||||
ProductDTO getProductDTOById(String productIdentifier);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import de.rwu.easydrop.util.Config;
|
||||
|
||||
/**
|
||||
* Factory for Data Sources.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public class DataSourceFactory {
|
||||
|
||||
/**
|
||||
* The data source config.
|
||||
*/
|
||||
private Config config;
|
||||
|
||||
/**
|
||||
* @param newConfig the config to set
|
||||
*/
|
||||
public void setConfig(final Config newConfig) throws ConfigurationException {
|
||||
this.config = newConfig;
|
||||
this.config.loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newConfig
|
||||
*/
|
||||
public DataSourceFactory(final Config newConfig) throws ConfigurationException {
|
||||
this.setConfig(newConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Amazon Product Data Source.
|
||||
*
|
||||
* @return AmazonProductDataSource
|
||||
*/
|
||||
public AmazonProductDataSource createAmazonProductDataSource() {
|
||||
String apiUrl = config.getProperty("AMAZON_API_URL");
|
||||
String apiKey = config.getProperty("AMAZON_API_KEY");
|
||||
return new AmazonProductDataSource(apiUrl, apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an eBay Item Data Source.
|
||||
*
|
||||
* @return EbayItemDataSource
|
||||
*/
|
||||
public EbayItemDataSource createEbayItemDataSource() {
|
||||
String apiUrl = config.getProperty("EBAY_API_URL");
|
||||
String apiKey = config.getProperty("EBAY_API_KEY");
|
||||
return new EbayItemDataSource(apiUrl, apiKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.PathNotFoundException;
|
||||
import com.jayway.jsonpath.ReadContext;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
|
||||
/**
|
||||
* Interface to an eBay data source.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public final class EbayItemDataSource extends AbstractDataSource {
|
||||
/**
|
||||
* Name of this data source.
|
||||
*/
|
||||
private static final String DATA_ORIGIN = "eBay";
|
||||
/**
|
||||
* Base URL to the eBay data source.
|
||||
*/
|
||||
private String baseUrl;
|
||||
/**
|
||||
* Credential key to authorize access.
|
||||
*/
|
||||
private String apiKey;
|
||||
|
||||
/**
|
||||
* Sets up instance with Base URL and API Key.
|
||||
*
|
||||
* @param newBaseUrl
|
||||
* @param newApiKey
|
||||
*/
|
||||
public EbayItemDataSource(final String newBaseUrl, final String newApiKey) {
|
||||
this.baseUrl = newBaseUrl;
|
||||
this.apiKey = newApiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param searchQuery Exact product name or other valid identifier.
|
||||
*/
|
||||
@Override
|
||||
protected URL createApiUrl(final String searchQuery) throws MalformedURLException {
|
||||
return new URL(baseUrl
|
||||
+ "/buy/browse/v1/item_summary/search?q="
|
||||
+ searchQuery
|
||||
+ "&limit=1&offset=0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriches a ProductDTO with API-gathered data.
|
||||
*
|
||||
* @param product Unfinished ProductDTO
|
||||
* @param json Product data
|
||||
* @return Finished ProductDTO
|
||||
*/
|
||||
protected ProductDTO buildProductDTO(final ProductDTO product, final String json) {
|
||||
String root = "$.itemSummaries[0].";
|
||||
ReadContext ctx = JsonPath.parse(json);
|
||||
|
||||
try {
|
||||
product.setDataOrigin(DATA_ORIGIN);
|
||||
product.setAvailable(
|
||||
ctx.read(root + "shippingOptions[0].guaranteedDelivery", boolean.class));
|
||||
product.setCurrentPrice(ctx.read(root + "price.value", double.class));
|
||||
product.setDeliveryPrice(
|
||||
ctx.read(root + "shippingOptions[0].shippingCost.value", double.class));
|
||||
product.setMerchant(ctx.read(root + "seller.username", String.class));
|
||||
} catch (PathNotFoundException e) {
|
||||
// Pass, allow incomplete ProductDTO to pass for later validation
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDataOrigin() {
|
||||
return DATA_ORIGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getApiKey() {
|
||||
return this.apiKey;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +1,52 @@
|
||||
package de.rwu.easydrop.api.dto;
|
||||
|
||||
import de.rwu.easydrop.util.FormattingUtil;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Product data transfer object.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Data
|
||||
public class ProductDTO {
|
||||
/**
|
||||
* Data source platform, like "Amazon".
|
||||
*/
|
||||
private String dataOrigin;
|
||||
|
||||
/**
|
||||
* @return the dataOrigin
|
||||
*/
|
||||
public String getDataOrigin() {
|
||||
return dataOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newDataOrigin the dataOrigin to set
|
||||
*/
|
||||
public void setDataOrigin(final String newDataOrigin) {
|
||||
this.dataOrigin = newDataOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform internal product identifier.
|
||||
*/
|
||||
private String productId;
|
||||
|
||||
/**
|
||||
* @return the productId
|
||||
*/
|
||||
public String getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current product price per piece in Euro.
|
||||
*/
|
||||
private double currentPrice;
|
||||
|
||||
/**
|
||||
* @return the currentPrice
|
||||
*/
|
||||
public double getCurrentPrice() {
|
||||
return currentPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newCurrentPrice the currentPrice to set
|
||||
*/
|
||||
public void setCurrentPrice(final double newCurrentPrice) {
|
||||
this.currentPrice = newCurrentPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of mercant offering the product on the platform.
|
||||
*/
|
||||
private String merchant;
|
||||
|
||||
/**
|
||||
* @return the merchant
|
||||
*/
|
||||
public String getMerchant() {
|
||||
return merchant;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newMerchant the merchant to set
|
||||
*/
|
||||
public void setMerchant(final String newMerchant) {
|
||||
this.merchant = newMerchant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional Cost for delivery in Euro.
|
||||
*/
|
||||
private double deliveryPrice;
|
||||
|
||||
/**
|
||||
* @return the deliveryPrice
|
||||
*/
|
||||
public double getDeliveryPrice() {
|
||||
return deliveryPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newDeliveryPrice the deliveryPrice to set
|
||||
*/
|
||||
public void setDeliveryPrice(final double newDeliveryPrice) {
|
||||
this.deliveryPrice = newDeliveryPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the product can be purchased at this point.
|
||||
*/
|
||||
private boolean available;
|
||||
|
||||
/**
|
||||
* @return the available
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newAvailable the available to set
|
||||
*/
|
||||
public void setAvailable(final boolean newAvailable) {
|
||||
this.available = newAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ProductDTO instance.
|
||||
*
|
||||
* @param newProductId Interal Product indetifier
|
||||
* @param newProductId Internal Product indetifier
|
||||
* @param newDataOrigin Data Origin
|
||||
*/
|
||||
public ProductDTO(final String newProductId, final String newDataOrigin) {
|
||||
this.productId = newProductId;
|
||||
this.dataOrigin = newDataOrigin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "ProductDTO{"
|
||||
+ productId + " from "
|
||||
+ merchant + " ("
|
||||
+ dataOrigin + ")"
|
||||
+ " at "
|
||||
+ FormattingUtil.formatEuro(currentPrice) + " (available: "
|
||||
+ (available ? "yes" : "no") + ")}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package de.rwu.easydrop.data.model;
|
||||
|
||||
/**
|
||||
* A Product.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
public class Product {
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Business objects.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.data.model;
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.rwu.easydrop.exception;
|
||||
|
||||
/**
|
||||
* Exception that signifies the data of a Product are invalid.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public class DataSourceException extends RuntimeException {
|
||||
/**
|
||||
* Throws an exception that signifies the data of a Product are invalid.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public DataSourceException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception that signifies the data of a Product are invalid.
|
||||
*
|
||||
* @param message
|
||||
* @param cause
|
||||
*/
|
||||
public DataSourceException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.rwu.easydrop.exception;
|
||||
|
||||
/**
|
||||
* Exception that signifies the data of a Product are invalid.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public class InvalidProductException extends RuntimeException {
|
||||
/**
|
||||
* Throws an exception that signifies the data of a Product are invalid.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public InvalidProductException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception that signifies the data of a Product are invalid.
|
||||
*
|
||||
* @param message
|
||||
* @param cause
|
||||
*/
|
||||
public InvalidProductException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Contains EasyDrop-related custom exceptions.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
package de.rwu.easydrop.exception;
|
||||
53
src/main/java/de/rwu/easydrop/model/Product.java
Normal file
53
src/main/java/de/rwu/easydrop/model/Product.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package de.rwu.easydrop.model;
|
||||
|
||||
import de.rwu.easydrop.util.FormattingUtil;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* A Product.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
@Data
|
||||
public class Product {
|
||||
/**
|
||||
* Data source platform, like "Amazon".
|
||||
*/
|
||||
private String dataOrigin;
|
||||
|
||||
/**
|
||||
* Platform internal product identifier.
|
||||
*/
|
||||
private String productId;
|
||||
|
||||
/**
|
||||
* Current product price per piece in Euro.
|
||||
*/
|
||||
private double currentPrice;
|
||||
|
||||
/**
|
||||
* Name of mercant offering the product on the platform.
|
||||
*/
|
||||
private String merchant;
|
||||
|
||||
/**
|
||||
* Additional Cost for delivery in Euro.
|
||||
*/
|
||||
private double deliveryPrice;
|
||||
|
||||
/**
|
||||
* Whether the product can be purchased at this point.
|
||||
*/
|
||||
private boolean available;
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "Product: ["
|
||||
+ productId + " from "
|
||||
+ merchant + " ("
|
||||
+ dataOrigin + ")"
|
||||
+ " at "
|
||||
+ FormattingUtil.formatEuro(currentPrice) + " (available: "
|
||||
+ (available ? "yes" : "no") + ")]";
|
||||
}
|
||||
}
|
||||
6
src/main/java/de/rwu/easydrop/model/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/model/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Business objects.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
package de.rwu.easydrop.model;
|
||||
@@ -1,14 +1,44 @@
|
||||
package de.rwu.easydrop.service.mapping;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.model.Product;
|
||||
|
||||
/**
|
||||
* Maps between Product, ProductDAO and ProductDTO.
|
||||
*
|
||||
* TODO implement
|
||||
* @since 0.2.0
|
||||
*
|
||||
* @see Product
|
||||
* @see ProductDTO
|
||||
* @see ProductDAO
|
||||
*/
|
||||
public class ProductMapper {
|
||||
public final class ProductMapper {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent unwanted instantiation.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
private ProductMapper() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException("This is a mapping class, don't instantiate it.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Product object from a corresponding DTO.
|
||||
*
|
||||
* @param dto Product Data Transfer Object
|
||||
* @return Product
|
||||
*/
|
||||
public static Product mapProductFromDTO(final ProductDTO dto) {
|
||||
Product product = new Product();
|
||||
|
||||
product.setAvailable(dto.isAvailable());
|
||||
product.setCurrentPrice(dto.getCurrentPrice());
|
||||
product.setDataOrigin(dto.getDataOrigin());
|
||||
product.setDeliveryPrice(dto.getDeliveryPrice());
|
||||
product.setMerchant(dto.getMerchant());
|
||||
product.setProductId(dto.getProductId());
|
||||
|
||||
return product;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Maps different formats of corresponding objects.
|
||||
*
|
||||
* TODO implement
|
||||
* @since 0.2.0
|
||||
*/
|
||||
package de.rwu.easydrop.service.mapping;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package de.rwu.easydrop.service.retriever;
|
||||
|
||||
import de.rwu.easydrop.api.client.AmazonProductDataSource;
|
||||
import de.rwu.easydrop.api.client.DataSourceFactory;
|
||||
import de.rwu.easydrop.api.client.EbayItemDataSource;
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.model.Product;
|
||||
import de.rwu.easydrop.service.mapping.ProductMapper;
|
||||
import de.rwu.easydrop.service.validation.ProductValidator;
|
||||
|
||||
/**
|
||||
* Retrieves Product Objects from various data sources.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public class ProductRetriever {
|
||||
/**
|
||||
* Data source factory.
|
||||
*/
|
||||
private DataSourceFactory dataSourceFactory;
|
||||
|
||||
/**
|
||||
* @param newDataSourceFactory the dataSourceFactory to set
|
||||
*/
|
||||
public void setDataSourceFactory(final DataSourceFactory newDataSourceFactory) {
|
||||
this.dataSourceFactory = newDataSourceFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newDataSourceFactory
|
||||
*/
|
||||
public ProductRetriever(final DataSourceFactory newDataSourceFactory) {
|
||||
this.setDataSourceFactory(newDataSourceFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a product from Amazon.
|
||||
*
|
||||
* @param asin Product identifier
|
||||
* @return Product from Amazon
|
||||
*/
|
||||
public Product getProductFromAmazon(final String asin) {
|
||||
AmazonProductDataSource dataSource = dataSourceFactory.createAmazonProductDataSource();
|
||||
ProductDTO dto = dataSource.getProductDTOById(asin);
|
||||
|
||||
Product product = ProductMapper.mapProductFromDTO(dto);
|
||||
ProductValidator.validate(product);
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a product from eBay.
|
||||
*
|
||||
* @param query Product search query
|
||||
* @return Product from eBay
|
||||
*/
|
||||
public Product getProductFromEbay(final String query) {
|
||||
EbayItemDataSource dataSource = dataSourceFactory.createEbayItemDataSource();
|
||||
ProductDTO dto = dataSource.getProductDTOById(query);
|
||||
|
||||
Product product = ProductMapper.mapProductFromDTO(dto);
|
||||
ProductValidator.validate(product);
|
||||
|
||||
return product;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Retrieves Objects from a data source.
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
package de.rwu.easydrop.service.retriever;
|
||||
@@ -1,10 +1,56 @@
|
||||
package de.rwu.easydrop.service.validation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import de.rwu.easydrop.exception.InvalidProductException;
|
||||
import de.rwu.easydrop.model.Product;
|
||||
|
||||
/**
|
||||
* Confirms validity of Product data.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public class ProductValidator {
|
||||
public final class ProductValidator {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent unwanted instantiation.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
private ProductValidator() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException("This is a validator class, don't instantiate it.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure a Product does not contain invalid information.
|
||||
*
|
||||
* @param product the Product
|
||||
*/
|
||||
public static void validate(final Product product) {
|
||||
if (product.getCurrentPrice() == 0.00) {
|
||||
throw new InvalidProductException("Current price cannot be 0.00");
|
||||
}
|
||||
if (!isInValidDataOrigins(product.getDataOrigin())) {
|
||||
throw new InvalidProductException("Unknown data source");
|
||||
}
|
||||
if (product.getProductId().equals("")) {
|
||||
throw new InvalidProductException("Product ID cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a dataOrigin is within the set of valid ones.
|
||||
*
|
||||
* @param dataOrigin like "Amazon"
|
||||
* @return true if valid
|
||||
*/
|
||||
public static boolean isInValidDataOrigins(final String dataOrigin) {
|
||||
Set<String> validOrigins = new HashSet<>();
|
||||
|
||||
validOrigins.add("Amazon");
|
||||
validOrigins.add("eBay");
|
||||
|
||||
return validOrigins.contains(dataOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Supports validation processes.
|
||||
*
|
||||
* TODO implement
|
||||
* @since 0.2.0
|
||||
*/
|
||||
package de.rwu.easydrop.service.validation;
|
||||
|
||||
@@ -56,7 +56,7 @@ public final class Config {
|
||||
*/
|
||||
public static Config getInstance() {
|
||||
if (instance == null) {
|
||||
return new Config();
|
||||
instance = new Config();
|
||||
}
|
||||
|
||||
return instance;
|
||||
@@ -109,4 +109,11 @@ public final class Config {
|
||||
public void setProperty(final String key, final String value) {
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the config's properties.
|
||||
*/
|
||||
public void reset() {
|
||||
properties = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,14 @@ public final class FormattingUtil {
|
||||
public static String formatEuro(final double amount) {
|
||||
return String.format(Locale.GERMAN, "%,.2f", amount) + " €";
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a string URL ready. For now, only spaces are replaced.
|
||||
*
|
||||
* @param str
|
||||
* @return URL-ready string
|
||||
*/
|
||||
public static String urlEncode(final String str) {
|
||||
return str.replace(" ", "+");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<configuration>
|
||||
<logger name="com.jayway.jsonpath" level="OFF" />
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
@@ -6,7 +8,7 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.exception.DataSourceException;
|
||||
|
||||
class AmazonProductDataSourceTest {
|
||||
|
||||
@@ -150,6 +151,7 @@ class AmazonProductDataSourceTest {
|
||||
|
||||
AmazonProductDataSource dataSource = mock(AmazonProductDataSource.class);
|
||||
URL mockURL = mock(URL.class);
|
||||
when(dataSource.getDataOrigin()).thenReturn(demoDataOrigin);
|
||||
when(dataSource.createApiUrl(demoProductId)).thenReturn(mockURL);
|
||||
when(dataSource.getProductDTOById(demoProductId)).thenCallRealMethod();
|
||||
HttpURLConnection mockConnection = mock(HttpURLConnection.class);
|
||||
@@ -157,7 +159,7 @@ class AmazonProductDataSourceTest {
|
||||
when(mockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND);
|
||||
|
||||
// Invoke the method and verify the exception
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
DataSourceException exception = assertThrows(DataSourceException.class, () -> {
|
||||
dataSource.getProductDTOById(demoProductId);
|
||||
});
|
||||
|
||||
@@ -170,6 +172,7 @@ class AmazonProductDataSourceTest {
|
||||
// Set up the test environment
|
||||
AmazonProductDataSource dataSource = mock(AmazonProductDataSource.class);
|
||||
URL mockURL = mock(URL.class);
|
||||
when(dataSource.getDataOrigin()).thenReturn(demoDataOrigin);
|
||||
when(dataSource.createApiUrl(demoProductId)).thenReturn(mockURL);
|
||||
when(dataSource.getProductDTOById(demoProductId)).thenCallRealMethod();
|
||||
when(dataSource.buildProductDTO(any(), anyString())).thenCallRealMethod();
|
||||
@@ -179,11 +182,23 @@ class AmazonProductDataSourceTest {
|
||||
when(mockConnection.getInputStream()).thenThrow(new IOException());
|
||||
|
||||
// Invoke the method and verify the exception
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
DataSourceException exception = assertThrows(DataSourceException.class, () -> {
|
||||
dataSource.getProductDTOById(demoProductId);
|
||||
});
|
||||
|
||||
// Verify the exception message
|
||||
assertEquals("Couldn't fulfill Amazon API request", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDataOrigin_ReturnsExpectedDataOrigin() {
|
||||
String dataOrigin = demoDataSource.getDataOrigin();
|
||||
assertEquals(demoDataOrigin, dataOrigin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getApiKey_ReturnsExpectedApiKey() {
|
||||
String apiKey = demoDataSource.getApiKey();
|
||||
assertEquals(demoApiKey, apiKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import de.rwu.easydrop.util.Config;
|
||||
|
||||
class DataSourceFactoryTest {
|
||||
@Mock
|
||||
private Config config;
|
||||
|
||||
private DataSourceFactory dataSourceFactory;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws ConfigurationException {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(config.getProperty("AMAZON_API_URL")).thenReturn("https://api.amazon.com");
|
||||
when(config.getProperty("AMAZON_API_KEY")).thenReturn("amazon-api-key");
|
||||
when(config.getProperty("EBAY_API_URL")).thenReturn("https://api.ebay.com");
|
||||
when(config.getProperty("EBAY_API_KEY")).thenReturn("ebay-api-key");
|
||||
dataSourceFactory = new DataSourceFactory(config);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAmazonProductDataSource_ReturnsAmazonProductDataSource() {
|
||||
// Act
|
||||
AmazonProductDataSource dataSource = dataSourceFactory.createAmazonProductDataSource();
|
||||
|
||||
// Assert
|
||||
assertEquals("amazon-api-key", dataSource.getApiKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createEbayItemDataSource_ReturnsEbayItemDataSource() {
|
||||
// Act
|
||||
EbayItemDataSource dataSource = dataSourceFactory.createEbayItemDataSource();
|
||||
|
||||
// Assert
|
||||
assertEquals("ebay-api-key", dataSource.getApiKey());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.exception.DataSourceException;
|
||||
|
||||
class EbayItemDataSourceTest {
|
||||
private EbayItemDataSource demoDataSource;
|
||||
|
||||
private static String demoApiKey = "my-api-key";
|
||||
private static String demoApiUrl = "https://www.example.com/api";
|
||||
private static String demoDataOrigin = "eBay";
|
||||
private static String demoQuery = "iPhone";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
demoDataSource = new EbayItemDataSource(demoApiUrl, demoApiKey);
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructor() {
|
||||
// Assert
|
||||
try {
|
||||
Field baseUrlField = EbayItemDataSource.class.getDeclaredField("baseUrl");
|
||||
baseUrlField.setAccessible(true);
|
||||
Assertions.assertEquals(demoApiUrl, baseUrlField.get(demoDataSource));
|
||||
|
||||
Field apiKeyField = EbayItemDataSource.class.getDeclaredField("apiKey");
|
||||
apiKeyField.setAccessible(true);
|
||||
Assertions.assertEquals(demoApiKey, apiKeyField.get(demoDataSource));
|
||||
} catch (NoSuchFieldException e) {
|
||||
Assertions.fail();
|
||||
} catch (IllegalAccessException e) {
|
||||
Assertions.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createApiUrl_ValidSearchQuery_ReturnsValidUrl() throws MalformedURLException {
|
||||
URL apiUrl = demoDataSource.createApiUrl(demoQuery);
|
||||
|
||||
assertNotNull(apiUrl);
|
||||
assertEquals("https://www.example.com/api/buy/browse/v1/item_summary/search?q=iPhone&limit=1&offset=0",
|
||||
apiUrl.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildProductDTO_ValidJson_ReturnsValidProductDTO() {
|
||||
ProductDTO product = new ProductDTO(demoQuery, demoDataOrigin);
|
||||
String json = "{\"itemSummaries\":[{\"shippingOptions\":[{\"guaranteedDelivery\":true,\"shippingCost\":{\"value\":10}}],\"price\":{\"value\":999.99},\"seller\":{\"username\":\"seller123\"}}]}";
|
||||
|
||||
ProductDTO result = demoDataSource.buildProductDTO(product, json);
|
||||
|
||||
assertEquals(demoDataOrigin, result.getDataOrigin());
|
||||
assertEquals(true, result.isAvailable());
|
||||
assertEquals(999.99, result.getCurrentPrice());
|
||||
assertEquals(10.0, result.getDeliveryPrice());
|
||||
assertEquals("seller123", result.getMerchant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildProductDTO_InvalidJson_ReturnsProductDTOWithDefaults() {
|
||||
ProductDTO product = new ProductDTO(demoQuery, demoDataOrigin);
|
||||
String json = "{\"itemSummaries\":[]}"; // Empty JSON to simulate missing data
|
||||
|
||||
ProductDTO result = demoDataSource.buildProductDTO(product, json);
|
||||
|
||||
assertEquals("eBay", result.getDataOrigin());
|
||||
assertEquals(false, result.isAvailable()); // Default value for boolean
|
||||
assertEquals(0.0, result.getCurrentPrice()); // Default value for double
|
||||
assertEquals(0.0, result.getDeliveryPrice()); // Default value for double
|
||||
assertEquals(null, result.getMerchant()); // Default value for String
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDataOrigin_ReturnsExpectedDataOrigin() {
|
||||
String dataOrigin = demoDataSource.getDataOrigin();
|
||||
assertEquals(demoDataOrigin, dataOrigin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getApiKey_ReturnsExpectedApiKey() {
|
||||
String apiKey = demoDataSource.getApiKey();
|
||||
assertEquals(demoApiKey, apiKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProductDTOById_failedRequest() throws IOException {
|
||||
// Set up the test environment
|
||||
|
||||
EbayItemDataSource dataSource = mock(EbayItemDataSource.class);
|
||||
URL mockURL = mock(URL.class);
|
||||
when(dataSource.getDataOrigin()).thenReturn(demoDataOrigin);
|
||||
when(dataSource.createApiUrl(demoQuery)).thenReturn(mockURL);
|
||||
when(dataSource.getProductDTOById(demoQuery)).thenCallRealMethod();
|
||||
HttpURLConnection mockConnection = mock(HttpURLConnection.class);
|
||||
when(mockURL.openConnection()).thenReturn(mockConnection);
|
||||
when(mockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND);
|
||||
|
||||
// Invoke the method and verify the exception
|
||||
DataSourceException exception = assertThrows(DataSourceException.class, () -> {
|
||||
dataSource.getProductDTOById(demoQuery);
|
||||
});
|
||||
|
||||
// Verify the exception message
|
||||
assertEquals("Nothing found: eBay API responded with error code 404", exception.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,56 @@
|
||||
package de.rwu.easydrop.api.dto;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ProductDTOTest {
|
||||
|
||||
@Test
|
||||
void testToString1() {
|
||||
ProductDTO product1 = new ProductDTO("12345", "Amazon");
|
||||
product1.setMerchant("Merchant A");
|
||||
product1.setCurrentPrice(19.99);
|
||||
product1.setAvailable(true);
|
||||
void constructor_SetsProductIdAndDataOrigin() {
|
||||
// Arrange
|
||||
String productId = "12345";
|
||||
String dataOrigin = "Amazon";
|
||||
|
||||
String expectedString1 = "ProductDTO{12345 from Merchant A (Amazon) at 19,99 € (available: yes)}";
|
||||
String result1 = product1.toString();
|
||||
// Act
|
||||
ProductDTO productDTO = new ProductDTO(productId, dataOrigin);
|
||||
|
||||
assertEquals(expectedString1, result1);
|
||||
// Assert
|
||||
assertEquals(productId, productDTO.getProductId());
|
||||
assertEquals(dataOrigin, productDTO.getDataOrigin());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString2() {
|
||||
ProductDTO product2 = new ProductDTO("67890", "eBay");
|
||||
product2.setMerchant("Merchant B");
|
||||
product2.setCurrentPrice(9.99);
|
||||
product2.setAvailable(false);
|
||||
void gettersAndSetters_WorkAsExpected() {
|
||||
// Arrange
|
||||
ProductDTO productDTO = new ProductDTO("12345", "Amazon");
|
||||
|
||||
String expectedString2 = "ProductDTO{67890 from Merchant B (eBay) at 9,99 € (available: no)}";
|
||||
String result2 = product2.toString();
|
||||
// Act and Assert
|
||||
assertEquals("12345", productDTO.getProductId());
|
||||
assertEquals("Amazon", productDTO.getDataOrigin());
|
||||
|
||||
assertEquals(expectedString2, result2);
|
||||
// Modify fields
|
||||
productDTO.setProductId("54321");
|
||||
productDTO.setDataOrigin("eBay");
|
||||
|
||||
// Assert
|
||||
assertEquals("54321", productDTO.getProductId());
|
||||
assertEquals("eBay", productDTO.getDataOrigin());
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultConstructor_SetsDefaultValues() {
|
||||
// Act
|
||||
ProductDTO productDTO = new ProductDTO(null, null);
|
||||
|
||||
// Assert
|
||||
assertNull(productDTO.getProductId());
|
||||
assertNull(productDTO.getDataOrigin());
|
||||
assertEquals(0.0, productDTO.getCurrentPrice());
|
||||
assertNull(productDTO.getMerchant());
|
||||
assertEquals(0.0, productDTO.getDeliveryPrice());
|
||||
assertFalse(productDTO.isAvailable());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.rwu.easydrop.exception;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DataSourceExceptionTest {
|
||||
|
||||
@Test
|
||||
void constructor_WithMessage_SetsMessage() {
|
||||
// Arrange
|
||||
String message = "Data source error";
|
||||
|
||||
// Act
|
||||
DataSourceException exception = new DataSourceException(message);
|
||||
|
||||
// Assert
|
||||
assertEquals(message, exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructor_WithMessageAndCause_SetsMessageAndCause() {
|
||||
// Arrange
|
||||
String message = "Data source error";
|
||||
Throwable cause = new IllegalArgumentException("Invalid argument");
|
||||
|
||||
// Act
|
||||
DataSourceException exception = new DataSourceException(message, cause);
|
||||
|
||||
// Assert
|
||||
assertEquals(message, exception.getMessage());
|
||||
assertEquals(cause, exception.getCause());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructor_WithNullMessage_SetsNullMessage() {
|
||||
// Act
|
||||
DataSourceException exception = new DataSourceException(null);
|
||||
|
||||
// Assert
|
||||
assertEquals(null, exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructor_WithNullCause_SetsNullCause() {
|
||||
// Act
|
||||
DataSourceException exception = new DataSourceException("Data source error", null);
|
||||
|
||||
// Assert
|
||||
assertEquals(null, exception.getCause());
|
||||
}
|
||||
|
||||
@Test
|
||||
void throw_DataSourceException() {
|
||||
// Act and Assert
|
||||
assertThrows(DataSourceException.class, () -> {
|
||||
throw new DataSourceException("Data source error");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.rwu.easydrop.exception;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class InvalidProductExceptionTest {
|
||||
|
||||
@Test
|
||||
void constructor_WithMessage_SetsMessage() {
|
||||
// Arrange
|
||||
String message = "Invalid product data";
|
||||
|
||||
// Act
|
||||
InvalidProductException exception = new InvalidProductException(message);
|
||||
|
||||
// Assert
|
||||
assertEquals(message, exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructor_WithMessageAndCause_SetsMessageAndCause() {
|
||||
// Arrange
|
||||
String message = "Invalid product data";
|
||||
Throwable cause = new IllegalArgumentException("Invalid argument");
|
||||
|
||||
// Act
|
||||
InvalidProductException exception = new InvalidProductException(message, cause);
|
||||
|
||||
// Assert
|
||||
assertEquals(message, exception.getMessage());
|
||||
assertEquals(cause, exception.getCause());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructor_WithNullMessage_SetsNullMessage() {
|
||||
// Act
|
||||
InvalidProductException exception = new InvalidProductException(null);
|
||||
|
||||
// Assert
|
||||
assertEquals(null, exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructor_WithNullCause_SetsNullCause() {
|
||||
// Act
|
||||
InvalidProductException exception = new InvalidProductException("Invalid product data", null);
|
||||
|
||||
// Assert
|
||||
assertEquals(null, exception.getCause());
|
||||
}
|
||||
|
||||
@Test
|
||||
void throw_InvalidProductException() {
|
||||
// Act and Assert
|
||||
assertThrows(InvalidProductException.class, () -> {
|
||||
throw new InvalidProductException("Invalid product data");
|
||||
});
|
||||
}
|
||||
}
|
||||
75
src/test/java/de/rwu/easydrop/model/ProductTest.java
Normal file
75
src/test/java/de/rwu/easydrop/model/ProductTest.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package de.rwu.easydrop.model;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ProductTest {
|
||||
@Test
|
||||
void testToString1() {
|
||||
Product product1 = new Product();
|
||||
product1.setDataOrigin("Amazon");
|
||||
product1.setProductId("12345");
|
||||
product1.setMerchant("Merchant A");
|
||||
product1.setCurrentPrice(19.99);
|
||||
product1.setAvailable(true);
|
||||
|
||||
String expectedString1 = "Product: [12345 from Merchant A (Amazon) at 19,99 € (available: yes)]";
|
||||
String result1 = product1.toString();
|
||||
|
||||
assertEquals(expectedString1, result1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString2() {
|
||||
Product product2 = new Product();
|
||||
product2.setDataOrigin("eBay");
|
||||
product2.setProductId("67890");
|
||||
product2.setMerchant("Merchant B");
|
||||
product2.setCurrentPrice(9.99);
|
||||
product2.setAvailable(false);
|
||||
|
||||
String expectedString2 = "Product: [67890 from Merchant B (eBay) at 9,99 € (available: no)]";
|
||||
String result2 = product2.toString();
|
||||
|
||||
assertEquals(expectedString2, result2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gettersAndSetters_WorkAsExpected() {
|
||||
// Arrange
|
||||
Product product = new Product();
|
||||
product.setDataOrigin("Amazon");
|
||||
product.setProductId("12345");
|
||||
product.setCurrentPrice(9.99);
|
||||
product.setMerchant("Example Merchant");
|
||||
product.setDeliveryPrice(2.50);
|
||||
product.setAvailable(true);
|
||||
|
||||
// Act and Assert
|
||||
assertEquals("Amazon", product.getDataOrigin());
|
||||
assertEquals("12345", product.getProductId());
|
||||
assertEquals(9.99, product.getCurrentPrice());
|
||||
assertEquals("Example Merchant", product.getMerchant());
|
||||
assertEquals(2.50, product.getDeliveryPrice());
|
||||
assertTrue(product.isAvailable());
|
||||
|
||||
// Modify fields
|
||||
product.setDataOrigin("eBay");
|
||||
product.setProductId("54321");
|
||||
product.setCurrentPrice(19.99);
|
||||
product.setMerchant("New Merchant");
|
||||
product.setDeliveryPrice(3.50);
|
||||
product.setAvailable(false);
|
||||
|
||||
// Assert
|
||||
assertEquals("eBay", product.getDataOrigin());
|
||||
assertEquals("54321", product.getProductId());
|
||||
assertEquals(19.99, product.getCurrentPrice());
|
||||
assertEquals("New Merchant", product.getMerchant());
|
||||
assertEquals(3.50, product.getDeliveryPrice());
|
||||
assertFalse(product.isAvailable());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package de.rwu.easydrop.service.mapping;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.model.Product;
|
||||
|
||||
class ProductMapperTest {
|
||||
@Test
|
||||
void testConstructorIsPrivate()
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
// Check for private constructor
|
||||
Constructor<ProductMapper> constructor = ProductMapper.class.getDeclaredConstructor();
|
||||
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
|
||||
|
||||
// Make sure exception is thrown when instantiating
|
||||
constructor.setAccessible(true);
|
||||
assertThrows(InvocationTargetException.class, () -> {
|
||||
constructor.newInstance();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapProductFromDTO_ReturnsProductWithMappedFields() {
|
||||
// Arrange
|
||||
ProductDTO dto = createProductDTO();
|
||||
|
||||
// Act
|
||||
Product product = ProductMapper.mapProductFromDTO(dto);
|
||||
|
||||
// Assert
|
||||
assertEquals(dto.isAvailable(), product.isAvailable());
|
||||
assertEquals(dto.getCurrentPrice(), product.getCurrentPrice());
|
||||
assertEquals(dto.getDataOrigin(), product.getDataOrigin());
|
||||
assertEquals(dto.getDeliveryPrice(), product.getDeliveryPrice());
|
||||
assertEquals(dto.getMerchant(), product.getMerchant());
|
||||
assertEquals(dto.getProductId(), product.getProductId());
|
||||
}
|
||||
|
||||
private ProductDTO createProductDTO() {
|
||||
ProductDTO dto = new ProductDTO("12345", "Amazon");
|
||||
dto.setAvailable(true);
|
||||
dto.setCurrentPrice(9.99);
|
||||
dto.setDeliveryPrice(2.50);
|
||||
dto.setMerchant("Example Merchant");
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package de.rwu.easydrop.service.retriever;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import de.rwu.easydrop.api.client.AmazonProductDataSource;
|
||||
import de.rwu.easydrop.api.client.DataSourceFactory;
|
||||
import de.rwu.easydrop.api.client.EbayItemDataSource;
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
import de.rwu.easydrop.model.Product;
|
||||
import de.rwu.easydrop.util.Config;
|
||||
|
||||
class ProductRetrieverTest {
|
||||
@Mock
|
||||
private Config config;
|
||||
@Mock
|
||||
private DataSourceFactory dataSourceFactory;
|
||||
@Mock
|
||||
private AmazonProductDataSource amazonDataSource;
|
||||
@Mock
|
||||
private EbayItemDataSource ebayDataSource;
|
||||
@Mock
|
||||
private ProductDTO productDTO;
|
||||
@Mock
|
||||
private Product product;
|
||||
|
||||
private ProductRetriever productRetriever;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws ConfigurationException {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(config.getProperty("AMAZON_API_URL")).thenReturn("https://api.amazon.com");
|
||||
when(config.getProperty("AMAZON_API_KEY")).thenReturn("amazon-api-key");
|
||||
dataSourceFactory.setConfig(config);
|
||||
productRetriever = new ProductRetriever(dataSourceFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProductFromAmazon_ReturnsProduct() {
|
||||
// Arrange
|
||||
String asin = "B01234ABC";
|
||||
when(dataSourceFactory.createAmazonProductDataSource()).thenReturn(amazonDataSource);
|
||||
when(amazonDataSource.getProductDTOById(asin)).thenReturn(productDTO);
|
||||
when(productDTO.getProductId()).thenReturn(asin);
|
||||
when(productDTO.getCurrentPrice()).thenReturn(9.99);
|
||||
when(productDTO.getDataOrigin()).thenReturn("Amazon");
|
||||
|
||||
// Act
|
||||
Product result = productRetriever.getProductFromAmazon(asin);
|
||||
|
||||
// Assert
|
||||
assertNotNull(result);
|
||||
assertEquals(asin, result.getProductId());
|
||||
assertEquals(9.99, result.getCurrentPrice());
|
||||
verify(amazonDataSource, times(1)).getProductDTOById(asin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProductFromEbay_ReturnsProduct() {
|
||||
// Arrange
|
||||
String productQuery = "MySearchQuery";
|
||||
when(dataSourceFactory.createEbayItemDataSource()).thenReturn(ebayDataSource);
|
||||
when(ebayDataSource.getProductDTOById(productQuery)).thenReturn(productDTO);
|
||||
when(productDTO.getProductId()).thenReturn(productQuery);
|
||||
when(productDTO.getCurrentPrice()).thenReturn(9.99);
|
||||
when(productDTO.getDataOrigin()).thenReturn("eBay");
|
||||
|
||||
// Act
|
||||
Product result = productRetriever.getProductFromEbay(productQuery);
|
||||
|
||||
// Assert
|
||||
assertNotNull(result);
|
||||
assertEquals(productQuery, result.getProductId());
|
||||
assertEquals(9.99, result.getCurrentPrice());
|
||||
verify(ebayDataSource, times(1)).getProductDTOById(productQuery);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package de.rwu.easydrop.service.validation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import de.rwu.easydrop.exception.InvalidProductException;
|
||||
import de.rwu.easydrop.model.Product;
|
||||
|
||||
class ProductValidatorTest {
|
||||
|
||||
@Test
|
||||
void testConstructorIsPrivate()
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
// Check for private constructor
|
||||
Constructor<ProductValidator> constructor = ProductValidator.class.getDeclaredConstructor();
|
||||
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
|
||||
|
||||
// Make sure exception is thrown when instantiating
|
||||
constructor.setAccessible(true);
|
||||
assertThrows(InvocationTargetException.class, () -> {
|
||||
constructor.newInstance();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate_ValidProduct_NoExceptionThrown() {
|
||||
// Arrange
|
||||
Product product = new Product();
|
||||
product.setCurrentPrice(9.99);
|
||||
product.setDataOrigin("Amazon");
|
||||
product.setProductId("12345");
|
||||
|
||||
// Act and Assert
|
||||
assertDoesNotThrow(() -> ProductValidator.validate(product));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValidDataOrigins_ValidDataOrigin_ReturnsTrue() {
|
||||
// Arrange
|
||||
String dataOrigin = "Amazon";
|
||||
|
||||
// Act
|
||||
boolean result = ProductValidator.isInValidDataOrigins(dataOrigin);
|
||||
|
||||
// Assert
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValidDataOrigins_InvalidDataOrigin_ReturnsFalse() {
|
||||
// Arrange
|
||||
String dataOrigin = "UnknownOrigin";
|
||||
|
||||
// Act
|
||||
boolean result = ProductValidator.isInValidDataOrigins(dataOrigin);
|
||||
|
||||
// Assert
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("invalidProductProvider")
|
||||
void validate_InvalidProduct_ThrowsInvalidProductException(Product product) {
|
||||
// Act and Assert
|
||||
assertThrows(InvalidProductException.class, () -> ProductValidator.validate(product));
|
||||
}
|
||||
|
||||
static Stream<Product> invalidProductProvider() {
|
||||
return Stream.of(
|
||||
createProductWithZeroPrice(),
|
||||
createProductWithUnknownDataOrigin(),
|
||||
createProductWithEmptyProductId());
|
||||
}
|
||||
|
||||
private static Product createProductWithZeroPrice() {
|
||||
Product product = new Product();
|
||||
product.setCurrentPrice(0.00);
|
||||
product.setDataOrigin("Amazon");
|
||||
product.setProductId("12345");
|
||||
return product;
|
||||
}
|
||||
|
||||
private static Product createProductWithUnknownDataOrigin() {
|
||||
Product product = new Product();
|
||||
product.setCurrentPrice(9.99);
|
||||
product.setDataOrigin("UnknownOrigin");
|
||||
product.setProductId("12345");
|
||||
return product;
|
||||
}
|
||||
|
||||
private static Product createProductWithEmptyProductId() {
|
||||
Product product = new Product();
|
||||
product.setCurrentPrice(9.99);
|
||||
product.setDataOrigin("Amazon");
|
||||
product.setProductId("");
|
||||
return product;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.rwu.easydrop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@@ -13,7 +14,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class ConfigImplTest {
|
||||
private Config config;
|
||||
private final static String TESTDATA_PATH = "testResources/testdata.properties";
|
||||
private final static String TESTDATA_PATH = "src/test/resources/testdata.properties";
|
||||
private final static String TESTDATA_KEY = "API_KEY";
|
||||
private final static String TESTDATA_VAL = "keyIsHere";
|
||||
|
||||
@@ -34,7 +35,9 @@ class ConfigImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProperty_ConfigNotLoaded() {
|
||||
void testGetProperty_ConfigNotLoaded() throws Exception {
|
||||
config.reset();
|
||||
|
||||
NoSuchElementException exception = assertThrows(NoSuchElementException.class, () -> {
|
||||
config.getProperty(TESTDATA_KEY);
|
||||
});
|
||||
@@ -74,7 +77,7 @@ class ConfigImplTest {
|
||||
@Test
|
||||
void testLoadConfigSuccessfully() {
|
||||
try {
|
||||
config.setConfigLocation("testResources/testdata.properties");
|
||||
config.setConfigLocation("src/test/resources/testdata.properties");
|
||||
config.loadConfig();
|
||||
assertEquals(TESTDATA_VAL, config.getProperty(TESTDATA_KEY));
|
||||
} catch (ConfigurationException e) {
|
||||
@@ -92,4 +95,19 @@ class ConfigImplTest {
|
||||
|
||||
assertEquals("Couldn't load required config file", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReset() throws ConfigurationException {
|
||||
config.setConfigLocation("src/test/resources/testdata.properties");
|
||||
config.loadConfig();
|
||||
|
||||
assertNotNull(config.getProperty(TESTDATA_KEY));
|
||||
config.reset();
|
||||
|
||||
NoSuchElementException exception = assertThrows(NoSuchElementException.class, () -> {
|
||||
config.getProperty(TESTDATA_KEY);
|
||||
});
|
||||
|
||||
assertEquals("Config has not been loaded", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.rwu.easydrop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
@@ -20,17 +21,18 @@ class ConfigTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstanceNull() {
|
||||
config = null;
|
||||
|
||||
void testGetInstance() {
|
||||
Config newConfig = Config.getInstance();
|
||||
assertNotNull(newConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstanceNotNull() {
|
||||
Config newConfig = Config.getInstance();
|
||||
assertNotNull(newConfig);
|
||||
void testGetInstanceEquality() {
|
||||
// Create "two" instances to check validity of Singleton pattern
|
||||
Config instance1 = Config.getInstance();
|
||||
Config instance2 = Config.getInstance();
|
||||
|
||||
assertSame(instance1, instance2, "Instances should be equal");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
1
src/test/resources/empty.properties
Normal file
1
src/test/resources/empty.properties
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
5
src/test/resources/testdata.properties
Normal file
5
src/test/resources/testdata.properties
Normal file
@@ -0,0 +1,5 @@
|
||||
API_KEY=keyIsHere
|
||||
AMAZON_API_URL=THE_AMAZON_API_URL
|
||||
AMAZON_API_KEY=THE_AMAZON_API_KEY
|
||||
EBAY_API_URL=THE_EBAY_API_URL
|
||||
EBAY_API_KEY=THE_EBAY_API_KEY
|
||||
@@ -1 +0,0 @@
|
||||
API_KEY=keyIsHere
|
||||
Reference in New Issue
Block a user