Merge branch 'dev' into 'main'
Release 0.1.0 Closes #39 See merge request team1/sandbox2!2
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,3 +1,9 @@
|
||||
###################################################################################################
|
||||
## Project Internal ###############################################################################
|
||||
###################################################################################################
|
||||
|
||||
config.properties
|
||||
|
||||
###################################################################################################
|
||||
## Visual Studio Code #############################################################################
|
||||
###################################################################################################
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## Use me!
|
||||
## 0.1.0
|
||||
|
||||
### Added
|
||||
|
||||
- (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)
|
||||
|
||||
544
SampleResponses/AmazonProductAPIModel.json
Normal file
544
SampleResponses/AmazonProductAPIModel.json
Normal file
@@ -0,0 +1,544 @@
|
||||
{
|
||||
"/products/2020-08-26/products/{productId}/offers": {
|
||||
"get": {
|
||||
"produces": ["application/json"],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The Amazon Standard Item Identifier (ASIN) for the product.",
|
||||
"in": "path",
|
||||
"name": "productId",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The region where the customer wants to purchase the product.",
|
||||
"enum": ["DE", "FR", "UK", "IT", "ES", "US", "CA", "JP"],
|
||||
"x-docgen-enum-table-extension": [
|
||||
{
|
||||
"value": "DE",
|
||||
"description": "Germany"
|
||||
},
|
||||
{
|
||||
"value": "FR",
|
||||
"description": "France"
|
||||
},
|
||||
{
|
||||
"value": "UK",
|
||||
"description": "United Kingdom"
|
||||
},
|
||||
{
|
||||
"value": "IT",
|
||||
"description": "Italy"
|
||||
},
|
||||
{
|
||||
"value": "ES",
|
||||
"description": "Spain"
|
||||
},
|
||||
{
|
||||
"value": "US",
|
||||
"description": "United States of America"
|
||||
},
|
||||
{
|
||||
"value": "CA",
|
||||
"description": "Canada"
|
||||
},
|
||||
{
|
||||
"value": "JP",
|
||||
"description": "Japan"
|
||||
}
|
||||
],
|
||||
"in": "query",
|
||||
"name": "productRegion",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "This field determines the region where to ship the product based on the value in the shippingPostalCode.",
|
||||
"in": "query",
|
||||
"name": "shippingRegion",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The locale of the request, in the form of an IETF language tag. Each ProductRegion supports one or more locales. This value should comply with java.util.Locale.",
|
||||
"pattern": "^[-_]+$",
|
||||
"in": "query",
|
||||
"name": "locale",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The shipping postal or zip code for customer's request. This parameter doesn't hold geocode.",
|
||||
"in": "query",
|
||||
"name": "shippingPostalCode",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The zero-based number of the page being requested. If not specified, a default value of 0 will be used. When passed, the value must be equal or greater than zero, and less than the number of pages returned in the response.",
|
||||
"in": "query",
|
||||
"name": "pageNumber",
|
||||
"required": false,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"description": "The number of items desired for each page in the response. If not specified, a default value of 24 will be used. Maximum items that can be fetched in single request is 24.",
|
||||
"in": "query",
|
||||
"name": "pageSize",
|
||||
"required": false,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"description": "A value that identifies the group within the business account that a customer belongs to. The customer can set this in Amazon Business account information. GroupTag is necessary only if the customer account belongs to more than one group.",
|
||||
"in": "query",
|
||||
"name": "groupTag",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"collectionFormat": "csv",
|
||||
"description": "A list of filter ids for use to query results.",
|
||||
"in": "query",
|
||||
"name": "filterIds",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The number of units the customer intends to purchase. This helps Amazon Business determines quantity-based discounts if an eligible offer is present. Defaults to 1.",
|
||||
"in": "query",
|
||||
"name": "quantity",
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"default": 1
|
||||
},
|
||||
{
|
||||
"description": "The email address of the customer requesting this resource",
|
||||
"in": "header",
|
||||
"name": "x-amz-user-email",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"collectionFormat": "csv",
|
||||
"description": "A list specifying the offer fields for inclusion in the response object. Excluding this query parameter results in the response including all inclusions.",
|
||||
"enum": [
|
||||
"ALL",
|
||||
"availability",
|
||||
"buyingGuidance",
|
||||
"buyingRestrictions",
|
||||
"condition",
|
||||
"fulfillmentType",
|
||||
"merchant",
|
||||
"price",
|
||||
"productCondition",
|
||||
"productConditionNote",
|
||||
"quantityLimits",
|
||||
"quantityPrice",
|
||||
"taxExclusivePrice",
|
||||
"deliveryInformation",
|
||||
"listPrice",
|
||||
"shippingOptions"
|
||||
],
|
||||
"in": "query",
|
||||
"name": "inclusionsForOffers",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-example": "/path?inclusionsForOffers=FOO,BAR"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Search success",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/OffersResult"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-amazon-spds-sandbox-behaviors": [
|
||||
{
|
||||
"request": {
|
||||
"parameters": {
|
||||
"productRegion": {
|
||||
"value": "DE"
|
||||
},
|
||||
"locale": {
|
||||
"value": "es_US"
|
||||
},
|
||||
"productId": {
|
||||
"value": "B07VDJ5RVF"
|
||||
},
|
||||
"pageNumber": {
|
||||
"value": "1"
|
||||
},
|
||||
"pageSize": {
|
||||
"value": "12"
|
||||
},
|
||||
"filterIds": {
|
||||
"value": ["NEW", "PRIME"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"offerCount": 2,
|
||||
"numberOfPages": 1,
|
||||
"featuredOffer": {
|
||||
"availability": "[availability]",
|
||||
"buyingGuidance": "NONE",
|
||||
"deliveryInformation": "[deliveryInformation]",
|
||||
"fulfillmentType": "AMAZON_FULFILLMENT",
|
||||
"offerId": "[offerId]",
|
||||
"merchant": {
|
||||
"name": "[merchantName]",
|
||||
"merchantId": "[merchantId]",
|
||||
"meanFeedbackRating": 5.0,
|
||||
"totalFeedbackCount": 1
|
||||
},
|
||||
"price": {
|
||||
"value": {
|
||||
"amount": 1.0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"priceType": "NEW"
|
||||
},
|
||||
"listPrice": {
|
||||
"value": {
|
||||
"amount": 2.0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"priceType": "NEW"
|
||||
},
|
||||
"taxExclusivePrice": {
|
||||
"taxExclusiveAmount": {
|
||||
"amount": 1.0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"displayString": "[taxExclusivePriceDisplayString]",
|
||||
"label": "[taxExclusivePriceLabel]"
|
||||
},
|
||||
"quantityLimits": {
|
||||
"minQuantity": 1,
|
||||
"maxQuantity": 6
|
||||
},
|
||||
"quantityPrice": {
|
||||
"quantityPriceTiers": [
|
||||
{
|
||||
"quantityDisplay": "string",
|
||||
"unitPrice": {
|
||||
"amount": 0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"minQuantity": 0,
|
||||
"price": {
|
||||
"value": {
|
||||
"amount": 0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"priceType": "NEW"
|
||||
},
|
||||
"savingMessage": "string",
|
||||
"taxExclusivePrice": {
|
||||
"taxExclusiveAmount": {
|
||||
"amount": 0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"displayString": "[taxExclusivePriceDisplayString]",
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"label": "[taxExclusivePriceLabel]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"productCondition": "NEW",
|
||||
"productConditionNote": "[productConditionNote]",
|
||||
"condition": {
|
||||
"conditionValue": "NEW",
|
||||
"conditionNote": "string",
|
||||
"subCondition": "ACCEPTABLE"
|
||||
},
|
||||
"buyingRestrictions": [],
|
||||
"shippingOptions": [
|
||||
{
|
||||
"shippingCost": {
|
||||
"value": {
|
||||
"amount": 9.0,
|
||||
"currencyCode": "USD"
|
||||
}
|
||||
},
|
||||
"deliveryRange": {
|
||||
"max": "2022-01-01T00:00:01.000Z",
|
||||
"min": "2022-01-01T00:00:01.000Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"offers": [
|
||||
{
|
||||
"offerId": "[offerId]",
|
||||
"merchant": {
|
||||
"name": "[merchantName]",
|
||||
"merchantId": "[merchantId]",
|
||||
"meanFeedbackRating": 5.0,
|
||||
"totalFeedbackCount": 1
|
||||
},
|
||||
"deliveryInformation": "[deliveryInformation]",
|
||||
"price": {
|
||||
"value": {
|
||||
"amount": 1.0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"priceType": "NEW"
|
||||
},
|
||||
"listPrice": {
|
||||
"value": {
|
||||
"amount": 2.0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"priceType": "NEW"
|
||||
},
|
||||
"taxExclusivePrice": {
|
||||
"taxExclusiveAmount": {
|
||||
"amount": 1.0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"displayString": "[taxExclusivePriceDisplayString]",
|
||||
"label": "[taxExclusivePriceLabel]"
|
||||
},
|
||||
"quantityLimits": {
|
||||
"minQuantity": 1,
|
||||
"maxQuantity": 6
|
||||
},
|
||||
"quantityPrice": {
|
||||
"quantityPriceTiers": [
|
||||
{
|
||||
"quantityDisplay": "string",
|
||||
"unitPrice": {
|
||||
"amount": 0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"minQuantity": 0,
|
||||
"price": {
|
||||
"value": {
|
||||
"amount": 0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"priceType": "NEW"
|
||||
},
|
||||
"savingMessage": "string",
|
||||
"taxExclusivePrice": {
|
||||
"taxExclusiveAmount": {
|
||||
"amount": 0,
|
||||
"currencyCode": "USD"
|
||||
},
|
||||
"displayString": "[taxExclusivePriceDisplayString]",
|
||||
"formattedPrice": "[formattedPrice]",
|
||||
"label": "[taxExclusivePriceLabel]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"productCondition": "NEW",
|
||||
"productConditionNote": "[productConditionNote]",
|
||||
"condition": {
|
||||
"conditionValue": "NEW",
|
||||
"conditionNote": "string",
|
||||
"subCondition": "ACCEPTABLE"
|
||||
},
|
||||
"availability": "[availability]",
|
||||
"fulfillmentType": "THIRD_PARTY",
|
||||
"buyingGuidance": "NONE",
|
||||
"buyingRestrictions": [],
|
||||
"shippingOptions": [
|
||||
{
|
||||
"shippingCost": {
|
||||
"value": {
|
||||
"amount": 9.0,
|
||||
"currencyCode": "USD"
|
||||
}
|
||||
},
|
||||
"deliveryRange": {
|
||||
"max": "2022-01-01T00:00:01.000Z",
|
||||
"min": "2022-01-01T00:00:01.000Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"filterGroups": [
|
||||
{
|
||||
"displayName": "[displayName]",
|
||||
"items": [
|
||||
{
|
||||
"displayName": "[itemDisplayName]",
|
||||
"id": "[itemId]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"400": {
|
||||
"description": "The service was unable to process the request. Reasons for the error are described in an error response object.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-amazon-spds-sandbox-behaviors": [
|
||||
{
|
||||
"request": {
|
||||
"parameters": {
|
||||
"productRegion": {
|
||||
"value": "US"
|
||||
},
|
||||
"productId": {
|
||||
"value": "B07VDJ5RVF"
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_REQUEST_PARAMETER",
|
||||
"message": "Missing required request parameters: [locale]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"401": {
|
||||
"description": "The request's authorization header isn't formatted correctly or doesn't contain a valid authorization input.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Indicates that access to the resource is forbidden. Possible reasons include Access Denied, Unauthorized, Expired Token, or Invalid Signature.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "The resource specified doesn't exist.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"429": {
|
||||
"description": "The frequency of requests was greater than allowed.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Encountered an unexpected condition which prevented the server from fulfilling the request.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"503": {
|
||||
"description": "Temporary overloading or maintenance of the server.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
},
|
||||
"headers": {
|
||||
"x-amzn-RateLimit-Limit": {
|
||||
"description": "The rate limit (requests per second) for this operation.",
|
||||
"type": "string"
|
||||
},
|
||||
"x-amzn-requestid": {
|
||||
"description": "A unique request reference identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["search"],
|
||||
"description": "Search for offers of a specific product.",
|
||||
"operationId": "searchOffersRequest",
|
||||
"summary": "Paginated and filtered search for offers of a specific product."
|
||||
}
|
||||
}
|
||||
}
|
||||
200
config/custom-checkstyle.xml
Normal file
200
config/custom-checkstyle.xml
Normal file
@@ -0,0 +1,200 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
|
||||
Modified version of sun coding conventions
|
||||
|
||||
Checkstyle configuration that checks the sun coding conventions from:
|
||||
|
||||
- the Java Language Specification at
|
||||
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
|
||||
|
||||
- the Sun Code Conventions at
|
||||
https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
|
||||
|
||||
- the Javadoc guidelines at
|
||||
https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
|
||||
|
||||
- the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
|
||||
|
||||
- some best practices
|
||||
|
||||
Checkstyle is very configurable. Be sure to read the documentation at
|
||||
https://checkstyle.org (or in your downloaded distribution).
|
||||
|
||||
Most Checks are configurable, be sure to consult the documentation.
|
||||
|
||||
To completely disable a check, just comment it out or delete it from the file.
|
||||
To suppress certain violations please review suppression filters.
|
||||
|
||||
Finally, it is worth reading the documentation.
|
||||
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
<!--
|
||||
If you set the basedir property below, then all reported file
|
||||
names will be relative to the specified directory. See
|
||||
https://checkstyle.org/config.html#Checker
|
||||
|
||||
<property name="basedir" value="${basedir}"/>
|
||||
-->
|
||||
<property name="severity" value="error" />
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml" />
|
||||
|
||||
<!-- Excludes all 'module-info.java' files -->
|
||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$" />
|
||||
</module>
|
||||
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
|
||||
default="checkstyle-suppressions.xml" />
|
||||
<property name="optional" value="true" />
|
||||
</module>
|
||||
|
||||
<!-- Checks that a package-info.java file exists for each package. -->
|
||||
<!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
|
||||
<module name="JavadocPackage" />
|
||||
|
||||
<!-- Checks whether files end with a new line. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
|
||||
<module name="NewlineAtEndOfFile" />
|
||||
|
||||
<!-- Checks that property files contain the same keys. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html#Translation -->
|
||||
<module name="Translation" />
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See https://checkstyle.org/config_sizes.html -->
|
||||
<module name="FileLength" />
|
||||
<module name="LineLength">
|
||||
<property name="max" value="100" />
|
||||
<property name="fileExtensions" value="java" />
|
||||
</module>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See https://checkstyle.org/config_whitespace.html -->
|
||||
<module name="FileTabCharacter" />
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html -->
|
||||
<module name="RegexpSingleline">
|
||||
<property name="format" value="\s+$" />
|
||||
<property name="minimum" value="0" />
|
||||
<property name="maximum" value="0" />
|
||||
<property name="message" value="Line has trailing spaces." />
|
||||
</module>
|
||||
|
||||
<!-- Checks for Headers -->
|
||||
<!-- See https://checkstyle.org/config_header.html -->
|
||||
<!-- <module name="Header"> -->
|
||||
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
|
||||
<!-- <property name="fileExtensions" value="java"/> -->
|
||||
<!-- </module> -->
|
||||
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See https://checkstyle.org/config_javadoc.html -->
|
||||
<module name="InvalidJavadocPosition" />
|
||||
<module name="JavadocMethod" />
|
||||
<module name="JavadocType" />
|
||||
<module name="JavadocVariable" />
|
||||
<module name="JavadocStyle" />
|
||||
<module name="MissingJavadocMethod" />
|
||||
|
||||
<!-- Checks for Naming Conventions. -->
|
||||
<!-- See https://checkstyle.org/config_naming.html -->
|
||||
<module name="ConstantName" />
|
||||
<module name="LocalFinalVariableName" />
|
||||
<module name="LocalVariableName" />
|
||||
<module name="MemberName" />
|
||||
<module name="MethodName" />
|
||||
<module name="PackageName" />
|
||||
<module name="ParameterName" />
|
||||
<module name="StaticVariableName" />
|
||||
<module name="TypeName" />
|
||||
|
||||
<!-- Checks for imports -->
|
||||
<!-- See https://checkstyle.org/config_imports.html -->
|
||||
<module name="AvoidStarImport" />
|
||||
<module name="IllegalImport" /> <!-- defaults to sun.* packages -->
|
||||
<module name="RedundantImport" />
|
||||
<module name="UnusedImports">
|
||||
<property name="processJavadoc" value="false" />
|
||||
</module>
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See https://checkstyle.org/config_sizes.html -->
|
||||
<module name="MethodLength" />
|
||||
<module name="ParameterNumber" />
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See https://checkstyle.org/config_whitespace.html -->
|
||||
<module name="EmptyForIteratorPad" />
|
||||
<module name="GenericWhitespace" />
|
||||
<module name="MethodParamPad" />
|
||||
<module name="NoWhitespaceAfter" />
|
||||
<module name="NoWhitespaceBefore" />
|
||||
<module name="OperatorWrap" />
|
||||
<module name="ParenPad" />
|
||||
<module name="TypecastParenPad" />
|
||||
<module name="WhitespaceAfter" />
|
||||
<module name="WhitespaceAround" />
|
||||
|
||||
<!-- Modifier Checks -->
|
||||
<!-- See https://checkstyle.org/config_modifier.html -->
|
||||
<module name="ModifierOrder" />
|
||||
<module name="RedundantModifier" />
|
||||
|
||||
<!-- Checks for blocks. You know, those {}'s -->
|
||||
<!-- See https://checkstyle.org/config_blocks.html -->
|
||||
<module name="AvoidNestedBlocks" />
|
||||
<module name="EmptyBlock" />
|
||||
<module name="LeftCurly" />
|
||||
<module name="NeedBraces" />
|
||||
<module name="RightCurly" />
|
||||
|
||||
<!-- Checks for common coding problems -->
|
||||
<!-- See https://checkstyle.org/config_coding.html -->
|
||||
<module name="EmptyStatement" />
|
||||
<module name="EqualsHashCode" />
|
||||
<module name="HiddenField" />
|
||||
<module name="IllegalInstantiation" />
|
||||
<module name="InnerAssignment" />
|
||||
<module name="MagicNumber" />
|
||||
<module name="MissingSwitchDefault" />
|
||||
<module name="MultipleVariableDeclarations" />
|
||||
<module name="SimplifyBooleanExpression" />
|
||||
<module name="SimplifyBooleanReturn" />
|
||||
|
||||
<!-- Checks for class design -->
|
||||
<!-- See https://checkstyle.org/config_design.html -->
|
||||
<module name="DesignForExtension" />
|
||||
<module name="FinalClass" />
|
||||
<module name="HideUtilityClassConstructor" />
|
||||
<module name="InterfaceIsType" />
|
||||
<module name="VisibilityModifier" />
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html -->
|
||||
<module name="ArrayTypeStyle" />
|
||||
<module name="FinalParameters" />
|
||||
<module name="TodoComment" />
|
||||
<module name="UpperEll" />
|
||||
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
|
||||
<module name="SuppressionXpathFilter">
|
||||
<property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}"
|
||||
default="checkstyle-xpath-suppressions.xml" />
|
||||
<property name="optional" value="true" />
|
||||
</module>
|
||||
|
||||
</module>
|
||||
|
||||
</module>
|
||||
3
config/demo.config.properties
Normal file
3
config/demo.config.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
# Amazon Credentials
|
||||
AMAZON_API_URL=
|
||||
AMAZON_API_KEY=
|
||||
12
config/logback.xml
Normal file
12
config/logback.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
40
pom.xml
40
pom.xml
@@ -5,27 +5,58 @@
|
||||
<groupId>de.rwu</groupId>
|
||||
<artifactId>easydrop</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<version>0.1.0</version>
|
||||
<name>EasyDrop</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.8.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.8.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20230227</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.4.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -39,6 +70,7 @@
|
||||
<id>checkstyle-check</id>
|
||||
<phase>verify</phase>
|
||||
<configuration>
|
||||
<configLocation>config/custom-checkstyle.xml</configLocation>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
|
||||
49
src/main/java/de/rwu/easydrop/Main.java
Normal file
49
src/main/java/de/rwu/easydrop/Main.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package de.rwu.easydrop;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.rwu.easydrop.api.client.AmazonProductDataSource;
|
||||
import de.rwu.easydrop.util.Config;
|
||||
|
||||
/**
|
||||
* Kickoff point for the service.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class Main {
|
||||
/**
|
||||
* Logger for main process.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
|
||||
|
||||
/**
|
||||
* Prevents unwanted instantiation.
|
||||
*/
|
||||
private Main() throws IllegalAccessException {
|
||||
throw new IllegalAccessException("Don't instantiate me! >:(");
|
||||
}
|
||||
|
||||
/**
|
||||
* Application entrypoint.
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
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;
|
||||
|
||||
AmazonProductDataSource amznSrc = new AmazonProductDataSource(amznBaseUrl, amznApiKey);
|
||||
try {
|
||||
testProduct = amznSrc.getProductDTOById("B096Y2TYKV").toString();
|
||||
LOGGER.info(testProduct);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Something went wrong :(", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
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 com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.PathNotFoundException;
|
||||
import com.jayway.jsonpath.ReadContext;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
|
||||
/**
|
||||
* Interface to an Amazon data source.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class AmazonProductDataSource implements DataSource {
|
||||
/**
|
||||
* Name of this data source.
|
||||
*/
|
||||
private static final String DATA_ORIGIN = "Amazon";
|
||||
/**
|
||||
* Base URL to the Amazon data source.
|
||||
*/
|
||||
private String baseUrl;
|
||||
/**
|
||||
* Credential key to authorize acccess.
|
||||
*/
|
||||
private String apiKey;
|
||||
/**
|
||||
* Product region parameter required for data access.
|
||||
*/
|
||||
private static final String PRODUCT_REGION = "DE";
|
||||
/**
|
||||
* Locale parameter required for data access.
|
||||
*/
|
||||
private static final String LOCALE = "de_DE";
|
||||
|
||||
/**
|
||||
* Sets up instance with Base URL and API Key.
|
||||
*
|
||||
* @param newBaseUrl
|
||||
* @param newApiKey
|
||||
*/
|
||||
public AmazonProductDataSource(final String newBaseUrl, final String newApiKey) {
|
||||
this.baseUrl = newBaseUrl;
|
||||
this.apiKey = newApiKey;
|
||||
}
|
||||
|
||||
@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) {
|
||||
String root = "$.featuredOffer.";
|
||||
ReadContext ctx = JsonPath.parse(json);
|
||||
|
||||
try {
|
||||
product.setDataOrigin(DATA_ORIGIN);
|
||||
product.setAvailable(
|
||||
ctx.read(root + "availability", String.class).equals("available"));
|
||||
product.setCurrentPrice(ctx.read(root + "price.value.amount", double.class));
|
||||
product.setDeliveryPrice(
|
||||
ctx.read(root + "shippingOptions[0].shippingCost.value.amount", double.class));
|
||||
product.setMerchant(ctx.read(root + "merchant.name", String.class));
|
||||
} catch (PathNotFoundException e) {
|
||||
// Pass, allow incomplete ProductDTO to pass for later validation
|
||||
}
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL createApiUrl(final String productId) throws MalformedURLException {
|
||||
return new URL(baseUrl
|
||||
+ "/products/2020-08-26/products/"
|
||||
+ productId
|
||||
+ "/offers?productRegion="
|
||||
+ PRODUCT_REGION
|
||||
+ "&locale="
|
||||
+ LOCALE);
|
||||
}
|
||||
}
|
||||
28
src/main/java/de/rwu/easydrop/api/client/DataSource.java
Normal file
28
src/main/java/de/rwu/easydrop/api/client/DataSource.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import de.rwu.easydrop.api.dto.ProductDTO;
|
||||
|
||||
/**
|
||||
* Universal interface to implement concrete APIs on.
|
||||
*/
|
||||
public interface DataSource {
|
||||
/**
|
||||
* Retrieves product info from the data source.
|
||||
*
|
||||
* @param productId ASIN
|
||||
* @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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* API client connectors for interaction with external data sources.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
package de.rwu.easydrop.api.client;
|
||||
139
src/main/java/de/rwu/easydrop/api/dto/ProductDTO.java
Normal file
139
src/main/java/de/rwu/easydrop/api/dto/ProductDTO.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package de.rwu.easydrop.api.dto;
|
||||
|
||||
import de.rwu.easydrop.util.FormattingUtil;
|
||||
|
||||
/**
|
||||
* Product data transfer object.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
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 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") + ")}";
|
||||
}
|
||||
}
|
||||
6
src/main/java/de/rwu/easydrop/api/dto/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/api/dto/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Data transfer objects for data derived from external sources.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
package de.rwu.easydrop.api.dto;
|
||||
6
src/main/java/de/rwu/easydrop/api/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/api/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Interaction with external APIs.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
package de.rwu.easydrop.api;
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.rwu.easydrop.data.connector;
|
||||
|
||||
/**
|
||||
* Allows connecting to a SQLite Database.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
public class DatabaseConnector {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Connectors for databases.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.data.connector;
|
||||
10
src/main/java/de/rwu/easydrop/data/dao/ProductDAO.java
Normal file
10
src/main/java/de/rwu/easydrop/data/dao/ProductDAO.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package de.rwu.easydrop.data.dao;
|
||||
|
||||
/**
|
||||
* Product data access object.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
public class ProductDAO {
|
||||
|
||||
}
|
||||
6
src/main/java/de/rwu/easydrop/data/dao/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/data/dao/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Data access objects for business objects created from persistence.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.data.dao;
|
||||
10
src/main/java/de/rwu/easydrop/data/model/Product.java
Normal file
10
src/main/java/de/rwu/easydrop/data/model/Product.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package de.rwu.easydrop.data.model;
|
||||
|
||||
/**
|
||||
* A Product.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
public class Product {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Business objects.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.data.model;
|
||||
6
src/main/java/de/rwu/easydrop/data/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/data/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Structure for business objects and persisting their info.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.data;
|
||||
@@ -1,16 +0,0 @@
|
||||
package de.rwu.easydrop.demo;
|
||||
|
||||
/**
|
||||
* Demo-Class.
|
||||
*/
|
||||
public final class DemoClass {
|
||||
/**
|
||||
* Demo-Method inverting a boolean.
|
||||
*
|
||||
* @param bool Input Boolean
|
||||
* @return Inverted Boolean
|
||||
*/
|
||||
public boolean invertBool(final boolean bool) {
|
||||
return !bool;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Dieses Paket beinhaltet ist zu Demo-Zwecken.
|
||||
*
|
||||
* @since 0.1-SNAPSHOT
|
||||
*/
|
||||
package de.rwu.easydrop.demo;
|
||||
6
src/main/java/de/rwu/easydrop/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* One of the Top 2 dropshipping platforms around.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
package de.rwu.easydrop;
|
||||
@@ -0,0 +1,14 @@
|
||||
package de.rwu.easydrop.service.mapping;
|
||||
|
||||
/**
|
||||
* Maps between Product, ProductDAO and ProductDTO.
|
||||
*
|
||||
* TODO implement
|
||||
*
|
||||
* @see Product
|
||||
* @see ProductDTO
|
||||
* @see ProductDAO
|
||||
*/
|
||||
public class ProductMapper {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Maps different formats of corresponding objects.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.service.mapping;
|
||||
6
src/main/java/de/rwu/easydrop/service/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/service/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Packages for supporting business logic.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.service;
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.rwu.easydrop.service.processing;
|
||||
|
||||
/**
|
||||
* Processes dropshipping orders.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
public class OrderManager {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Supports diverse business processes and enforces business rules.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.service.processing;
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.rwu.easydrop.service.validation;
|
||||
|
||||
/**
|
||||
* Confirms validity of Product data.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class ProductValidator {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Supports validation processes.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.service.validation;
|
||||
112
src/main/java/de/rwu/easydrop/util/Config.java
Normal file
112
src/main/java/de/rwu/easydrop/util/Config.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package de.rwu.easydrop.util;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
/**
|
||||
* Allows access to the config.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class Config {
|
||||
/**
|
||||
* Config file location.
|
||||
*/
|
||||
private String configLocation = "config/config.properties";
|
||||
|
||||
/**
|
||||
* @return the configLocation
|
||||
*/
|
||||
public String getConfigLocation() {
|
||||
return configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newConfigLocation the configLocation to set
|
||||
*/
|
||||
public void setConfigLocation(final String newConfigLocation) {
|
||||
configLocation = newConfigLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the config values.
|
||||
*/
|
||||
private Properties properties = null;
|
||||
/**
|
||||
* Singleton instance.
|
||||
*/
|
||||
private static Config instance = null;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent external instantiation.
|
||||
*/
|
||||
private Config() {
|
||||
// Do Nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current config instance.
|
||||
*
|
||||
* @return Config instance
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public static Config getInstance() {
|
||||
if (instance == null) {
|
||||
return new Config();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads config file values into the instance.
|
||||
*
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public void loadConfig() throws ConfigurationException {
|
||||
Properties newProps = new Properties();
|
||||
try (FileInputStream input = new FileInputStream(configLocation)) {
|
||||
newProps.load(input);
|
||||
properties = newProps;
|
||||
} catch (IOException e) {
|
||||
throw new ConfigurationException("Couldn't load required config file");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a config property by specified key.
|
||||
*
|
||||
* @param key Config Key, like "API_KEY"
|
||||
* @return Config value
|
||||
* @throws NoSuchElementException Required key missing
|
||||
*/
|
||||
public String getProperty(final String key) throws NoSuchElementException {
|
||||
String value = null;
|
||||
|
||||
try {
|
||||
value = properties.getProperty(key);
|
||||
} catch (NullPointerException e) {
|
||||
throw new NoSuchElementException("Config has not been loaded");
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException("Requested config value does not exist");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a config property loaded from file for current instance.
|
||||
*
|
||||
* @param key Config Key
|
||||
* @param value Property Value
|
||||
*/
|
||||
public void setProperty(final String key, final String value) {
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
}
|
||||
30
src/main/java/de/rwu/easydrop/util/FormattingUtil.java
Normal file
30
src/main/java/de/rwu/easydrop/util/FormattingUtil.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package de.rwu.easydrop.util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Helps format a bunch of things.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public final class FormattingUtil {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent unwanted instantiation.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
private FormattingUtil() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException("This is a utility class, don't instantiate it.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a price to the German Euro format.
|
||||
*
|
||||
* @param amount price
|
||||
* @return formatted price
|
||||
*/
|
||||
public static String formatEuro(final double amount) {
|
||||
return String.format(Locale.GERMAN, "%,.2f", amount) + " €";
|
||||
}
|
||||
}
|
||||
6
src/main/java/de/rwu/easydrop/util/package-info.java
Normal file
6
src/main/java/de/rwu/easydrop/util/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* General utility such as formatting helpers.
|
||||
*
|
||||
* TODO implement
|
||||
*/
|
||||
package de.rwu.easydrop.util;
|
||||
@@ -0,0 +1,189 @@
|
||||
package de.rwu.easydrop.api.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
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;
|
||||
|
||||
class AmazonProductDataSourceTest {
|
||||
|
||||
private AmazonProductDataSource demoDataSource;
|
||||
|
||||
private static String demoApiKey = "my-api-key";
|
||||
private static String demoApiUrl = "https://www.example.com/api";
|
||||
private static String demoDataOrigin = "Amazon";
|
||||
private static String demoProductId = "whateverId";
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
demoDataSource = new AmazonProductDataSource(
|
||||
demoApiUrl,
|
||||
demoApiKey);
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructor() {
|
||||
// Assert
|
||||
try {
|
||||
Field baseUrlField = AmazonProductDataSource.class.getDeclaredField("baseUrl");
|
||||
baseUrlField.setAccessible(true);
|
||||
Assertions.assertEquals(demoApiUrl, baseUrlField.get(demoDataSource));
|
||||
|
||||
Field apiKeyField = AmazonProductDataSource.class.getDeclaredField("apiKey");
|
||||
apiKeyField.setAccessible(true);
|
||||
Assertions.assertEquals(demoApiKey, apiKeyField.get(demoDataSource));
|
||||
} catch (NoSuchFieldException e) {
|
||||
Assertions.fail();
|
||||
} catch (IllegalAccessException e) {
|
||||
Assertions.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateApiUrl() throws MalformedURLException {
|
||||
// Test case 1
|
||||
String productId1 = "12345";
|
||||
URL expectedUrl1 = new URL(
|
||||
"https://www.example.com/api/products/2020-08-26/products/12345/offers?productRegion=DE&locale=de_DE");
|
||||
URL createdUrl1 = demoDataSource.createApiUrl(productId1);
|
||||
Assertions.assertEquals(expectedUrl1, createdUrl1);
|
||||
|
||||
// Test case 2
|
||||
String productId2 = "67890";
|
||||
URL expectedUrl2 = new URL(
|
||||
"https://www.example.com/api/products/2020-08-26/"
|
||||
+ "products/67890/offers?productRegion=DE&locale=de_DE");
|
||||
URL createdUrl2 = demoDataSource.createApiUrl(productId2);
|
||||
Assertions.assertEquals(expectedUrl2, createdUrl2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuildProductDTO_completeData() {
|
||||
// Set up the test environment
|
||||
String json = "{\"featuredOffer\": {\"availability\": \"available\"," +
|
||||
"\"price\": {\"value\": {\"amount\": 10.0}}," +
|
||||
"\"shippingOptions\": [{\"shippingCost\": {\"value\": {\"amount\": 2.5}}}], " +
|
||||
"\"merchant\": {\"name\": \"Merchant A\"}}}";
|
||||
ProductDTO product = new ProductDTO(demoProductId, demoDataOrigin);
|
||||
|
||||
// Invoke the method
|
||||
ProductDTO result = demoDataSource.buildProductDTO(product, json);
|
||||
|
||||
// Verify the product DTO properties
|
||||
assertEquals(demoDataOrigin, result.getDataOrigin());
|
||||
assertEquals(true, result.isAvailable());
|
||||
assertEquals(10.0, result.getCurrentPrice());
|
||||
assertEquals(2.5, result.getDeliveryPrice());
|
||||
assertEquals("Merchant A", result.getMerchant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuildProductDTO_incompleteData() {
|
||||
// Set up the test environment
|
||||
String json = "{\"otherOffer\": {\"availability\": \"available\"," +
|
||||
"\"price\": {\"value\": {\"amount\": 10.0}}," +
|
||||
"\"shippingOptions\": [{\"shippingCost\": {\"value\": {\"amount\": 2.5}}}], " +
|
||||
"\"merchant\": {\"name\": \"Merchant A\"}}}";
|
||||
ProductDTO product = new ProductDTO(demoProductId, demoDataOrigin);
|
||||
|
||||
// Invoke the method
|
||||
ProductDTO result = demoDataSource.buildProductDTO(product, json);
|
||||
|
||||
// Verify that the product DTO remains unchanged
|
||||
assertEquals(demoDataOrigin, result.getDataOrigin());
|
||||
assertEquals(false, result.isAvailable());
|
||||
assertEquals(0.0, result.getCurrentPrice());
|
||||
assertEquals(0.0, result.getDeliveryPrice());
|
||||
assertEquals(null, result.getMerchant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProductDTOById_successfulRequest() throws IOException {
|
||||
// Set up the test environment
|
||||
String mockResponse = "{\"featuredOffer\": {\"availability\": \"available\"," +
|
||||
"\"price\": {\"value\": {\"amount\": 10.0}}," +
|
||||
"\"shippingOptions\": [{\"shippingCost\": {\"value\": {\"amount\": 2.5}}}], " +
|
||||
"\"merchant\": {\"name\": \"Merchant A\"}}}";
|
||||
|
||||
AmazonProductDataSource dataSource = mock(AmazonProductDataSource.class);
|
||||
URL mockURL = mock(URL.class);
|
||||
when(dataSource.createApiUrl(demoProductId)).thenReturn(mockURL);
|
||||
when(dataSource.buildProductDTO(any(), anyString())).thenCallRealMethod();
|
||||
HttpURLConnection mockConnection = mock(HttpURLConnection.class);
|
||||
when(mockURL.openConnection()).thenReturn(mockConnection);
|
||||
when(mockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
|
||||
when(mockConnection.getInputStream()).thenReturn(new ByteArrayInputStream(mockResponse.getBytes()));
|
||||
when(dataSource.getProductDTOById(demoProductId)).thenCallRealMethod();
|
||||
|
||||
// Invoke the method
|
||||
ProductDTO result = dataSource.getProductDTOById(demoProductId);
|
||||
|
||||
// Verify the product DTO properties
|
||||
assertEquals(demoProductId, result.getProductId());
|
||||
assertEquals("Amazon", result.getDataOrigin());
|
||||
assertEquals(true, result.isAvailable());
|
||||
assertEquals(10.0, result.getCurrentPrice());
|
||||
assertEquals(2.5, result.getDeliveryPrice());
|
||||
assertEquals("Merchant A", result.getMerchant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProductDTOById_failedRequest() throws IOException {
|
||||
// Set up the test environment
|
||||
|
||||
AmazonProductDataSource dataSource = mock(AmazonProductDataSource.class);
|
||||
URL mockURL = mock(URL.class);
|
||||
when(dataSource.createApiUrl(demoProductId)).thenReturn(mockURL);
|
||||
when(dataSource.getProductDTOById(demoProductId)).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
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
dataSource.getProductDTOById(demoProductId);
|
||||
});
|
||||
|
||||
// Verify the exception message
|
||||
assertEquals("Nothing found: Amazon API responded with error code 404", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProductDTOById_ioException() throws IOException {
|
||||
// Set up the test environment
|
||||
AmazonProductDataSource dataSource = mock(AmazonProductDataSource.class);
|
||||
URL mockURL = mock(URL.class);
|
||||
when(dataSource.createApiUrl(demoProductId)).thenReturn(mockURL);
|
||||
when(dataSource.getProductDTOById(demoProductId)).thenCallRealMethod();
|
||||
when(dataSource.buildProductDTO(any(), anyString())).thenCallRealMethod();
|
||||
HttpURLConnection mockConnection = mock(HttpURLConnection.class);
|
||||
when(mockURL.openConnection()).thenReturn(mockConnection);
|
||||
when(mockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
|
||||
when(mockConnection.getInputStream()).thenThrow(new IOException());
|
||||
|
||||
// Invoke the method and verify the exception
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
dataSource.getProductDTOById(demoProductId);
|
||||
});
|
||||
|
||||
// Verify the exception message
|
||||
assertEquals("Couldn't fulfill Amazon API request", exception.getMessage());
|
||||
}
|
||||
}
|
||||
33
src/test/java/de/rwu/easydrop/api/dto/ProductDTOTest.java
Normal file
33
src/test/java/de/rwu/easydrop/api/dto/ProductDTOTest.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package de.rwu.easydrop.api.dto;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
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);
|
||||
|
||||
String expectedString1 = "ProductDTO{12345 from Merchant A (Amazon) at 19,99 € (available: yes)}";
|
||||
String result1 = product1.toString();
|
||||
|
||||
assertEquals(expectedString1, result1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString2() {
|
||||
ProductDTO product2 = new ProductDTO("67890", "eBay");
|
||||
product2.setMerchant("Merchant B");
|
||||
product2.setCurrentPrice(9.99);
|
||||
product2.setAvailable(false);
|
||||
|
||||
String expectedString2 = "ProductDTO{67890 from Merchant B (eBay) at 9,99 € (available: no)}";
|
||||
String result2 = product2.toString();
|
||||
|
||||
assertEquals(expectedString2, result2);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package de.rwu.easydrop.demo;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class DemoClassTest {
|
||||
private final DemoClass demoClass = new DemoClass();
|
||||
|
||||
@Test
|
||||
public void testInvertBoolTrue() {
|
||||
assertEquals("Inverting true should return false", false, demoClass.invertBool(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvertBoolFalse() {
|
||||
assertEquals("Inverting false should return true", true, demoClass.invertBool(false));
|
||||
}
|
||||
}
|
||||
95
src/test/java/de/rwu/easydrop/util/ConfigImplTest.java
Normal file
95
src/test/java/de/rwu/easydrop/util/ConfigImplTest.java
Normal file
@@ -0,0 +1,95 @@
|
||||
package de.rwu.easydrop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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_KEY = "API_KEY";
|
||||
private final static String TESTDATA_VAL = "keyIsHere";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
config = Config.getInstance();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProperty_ExistingKey() throws ConfigurationException {
|
||||
config.setConfigLocation(TESTDATA_PATH);
|
||||
config.loadConfig();
|
||||
config.setProperty(TESTDATA_KEY, TESTDATA_VAL);
|
||||
|
||||
String value = config.getProperty(TESTDATA_KEY);
|
||||
|
||||
assertEquals(TESTDATA_VAL, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProperty_ConfigNotLoaded() {
|
||||
NoSuchElementException exception = assertThrows(NoSuchElementException.class, () -> {
|
||||
config.getProperty(TESTDATA_KEY);
|
||||
});
|
||||
|
||||
assertEquals("Config has not been loaded", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetProperty_NonExistingKey() {
|
||||
try {
|
||||
config.setConfigLocation(TESTDATA_PATH);
|
||||
config.loadConfig();
|
||||
|
||||
NoSuchElementException exception = assertThrows(NoSuchElementException.class, () -> {
|
||||
config.getProperty("I_DONT_EXIST");
|
||||
});
|
||||
|
||||
assertEquals("Requested config value does not exist", exception.getMessage());
|
||||
} catch (ConfigurationException e) {
|
||||
fail("ConfigurationException should not be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetProperty() {
|
||||
try {
|
||||
config.setConfigLocation(TESTDATA_PATH);
|
||||
config.loadConfig();
|
||||
config.setProperty(TESTDATA_KEY, "12345");
|
||||
|
||||
assertEquals("12345", config.getProperty(TESTDATA_KEY));
|
||||
} catch (ConfigurationException e) {
|
||||
fail("ConfigurationException should not be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadConfigSuccessfully() {
|
||||
try {
|
||||
config.setConfigLocation("testResources/testdata.properties");
|
||||
config.loadConfig();
|
||||
assertEquals(TESTDATA_VAL, config.getProperty(TESTDATA_KEY));
|
||||
} catch (ConfigurationException e) {
|
||||
fail("ConfigurationException should not be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadConfigMissingFile() {
|
||||
config.setConfigLocation("path/that/doesnt/exist/config.properties");
|
||||
|
||||
ConfigurationException exception = assertThrows(ConfigurationException.class, () -> {
|
||||
config.loadConfig();
|
||||
});
|
||||
|
||||
assertEquals("Couldn't load required config file", exception.getMessage());
|
||||
}
|
||||
}
|
||||
42
src/test/java/de/rwu/easydrop/util/ConfigTest.java
Normal file
42
src/test/java/de/rwu/easydrop/util/ConfigTest.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package de.rwu.easydrop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
class ConfigTest {
|
||||
private Config config;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws ConfigurationException {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
config = spy(Config.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstanceNull() {
|
||||
config = null;
|
||||
|
||||
Config newConfig = Config.getInstance();
|
||||
assertNotNull(newConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstanceNotNull() {
|
||||
Config newConfig = Config.getInstance();
|
||||
assertNotNull(newConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetConfigLocation() {
|
||||
String newPath = "new/location/config.properties";
|
||||
config.setConfigLocation(newPath);
|
||||
assertEquals(newPath, config.getConfigLocation());
|
||||
}
|
||||
}
|
||||
58
src/test/java/de/rwu/easydrop/util/FormattingUtilTest.java
Normal file
58
src/test/java/de/rwu/easydrop/util/FormattingUtilTest.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package de.rwu.easydrop.util;
|
||||
|
||||
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.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class FormattingUtilTest {
|
||||
|
||||
@Test
|
||||
void testConstructorIsPrivate()
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
// Check for private constructor
|
||||
Constructor<FormattingUtil> constructor = FormattingUtil.class.getDeclaredConstructor();
|
||||
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
|
||||
|
||||
// Make sure exception is thrown when instantiating
|
||||
constructor.setAccessible(true);
|
||||
assertThrows(InvocationTargetException.class, () -> {
|
||||
constructor.newInstance();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFormatEuro_positiveAmount() {
|
||||
double amount = 1234.56;
|
||||
String expectedFormattedAmount = "1.234,56 €";
|
||||
|
||||
String formattedAmount = FormattingUtil.formatEuro(amount);
|
||||
|
||||
Assertions.assertEquals(expectedFormattedAmount, formattedAmount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFormatEuro_zeroAmount() {
|
||||
double amount = 0.0;
|
||||
String expectedFormattedAmount = "0,00 €";
|
||||
|
||||
String formattedAmount = FormattingUtil.formatEuro(amount);
|
||||
|
||||
Assertions.assertEquals(expectedFormattedAmount, formattedAmount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFormatEuro_negativeAmount() {
|
||||
double amount = -789.12;
|
||||
String expectedFormattedAmount = "-789,12 €";
|
||||
|
||||
String formattedAmount = FormattingUtil.formatEuro(amount);
|
||||
|
||||
Assertions.assertEquals(expectedFormattedAmount, formattedAmount);
|
||||
}
|
||||
}
|
||||
0
testResources/empty.properties
Normal file
0
testResources/empty.properties
Normal file
1
testResources/testdata.properties
Normal file
1
testResources/testdata.properties
Normal file
@@ -0,0 +1 @@
|
||||
API_KEY=keyIsHere
|
||||
Reference in New Issue
Block a user