├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README └── apaapi_logo.png ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── de │ └── codecrafters │ └── apaarb │ ├── AmazonProductAdvertisingApiRequestBuilder.java │ ├── AmazonWebServiceAuthentication.java │ ├── AmazonWebServiceLocation.java │ ├── Base64.java │ ├── ItemCategory.java │ ├── ItemCondition.java │ ├── ItemId.java │ ├── ItemInformation.java │ └── RequestUrlUtils.java └── test └── java └── de └── codecrafters └── apaarb ├── ItemLookupTest.java └── ItemSearchTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Gradle template 3 | .gradle 4 | build/ 5 | 6 | # Ignore Gradle GUI config 7 | gradle-app.setting 8 | 9 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 10 | !gradle-wrapper.jar 11 | 12 | # Cache of project 13 | .gradletasknamecache 14 | 15 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 16 | # gradle/wrapper/gradle-wrapper.properties 17 | ### Java template 18 | *.class 19 | 20 | # Mobile Tools for Java (J2ME) 21 | .mtj.tmp/ 22 | 23 | # Package Files # 24 | *.jar 25 | *.war 26 | *.ear 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | ### JetBrains template 31 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 32 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 33 | 34 | # User-specific stuff: 35 | .idea/ 36 | 37 | ## File-based project format: 38 | *.iws 39 | 40 | ## Plugin-specific files: 41 | 42 | # IntelliJ 43 | /out/ 44 | 45 | # mpeltonen/sbt-idea plugin 46 | .idea_modules/ 47 | 48 | # JIRA plugin 49 | atlassian-ide-plugin.xml 50 | 51 | # Crashlytics plugin (for Android Studio and IntelliJ) 52 | com_crashlytics_export_strings.xml 53 | crashlytics.properties 54 | crashlytics-build.properties 55 | fabric.properties 56 | local.properties 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: oraclejdk7 3 | sudo: false 4 | 5 | before_script: 6 | - chmod +x gradlew 7 | 8 | script: 9 | - ./gradlew build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder.svg?branch=master)](https://travis-ci.org/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder) 2 | # The Amazon-Product-Advertising-API URL Builder 3 | Simple builder in plain Java to create requests to the Amazon Product Advertising API. 4 | 5 | ![APA-API-Logo](https://raw.githubusercontent.com/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder/master/README/apaapi_logo.png) 6 | Amazon challenges all developers that want to use their Advertising API. Especially the creation of the request URL is 7 | pretty complex. Therefor I've created this URL Builder, which allows you to create the request URL without having to read 8 | the Product Advertisement API documentation or study the used hashing algorithms. 9 | 10 | #### Setup 11 | To use this library in your Java or Android project you can download it from jCenter (GroupId: de.codecrafters.apaarb, 12 | ArtifactId: apaarb) in the latest version. If you use gradle add `jCenter()` to your repositories (if not done yet) and 13 | specify the dependency. 14 | ``` 15 | dependencies { 16 | ... 17 | compile 'de.codecrafters.apaarb:apaarb:0.9.1' 18 | ... 19 | } 20 | ``` 21 | This library is written in plain java and does not depend on any other library. 22 | 23 | ## Item Identification 24 | There are multiple types of ID representations handled by Amazon. Namely they are ASIN, ISBN, UPX and EAN. So especially 25 | if you want to look up a specific item you have to give its ID. 26 | Theses IDs are represented inside this library by the `ItemId` class. To create them you simply call the matching create- 27 | method. 28 | ```java 29 | ItemId myAsin = ItemId.createAsin("AsinValue"); 30 | ItemId myEan = ItemId.createEan("EanValue"); 31 | ItemId myIsbn = ItemId.createIsbn("IsbnValue"); 32 | ItemId myUpx = ItemId.createUpx("UpxValue"); 33 | ``` 34 | After the creation you are able to get the ID value and type from the object. 35 | ```java 36 | String value = myId.getValue(); 37 | Type type = myId.getType(); 38 | ``` 39 | The `Type` is an enumeration inside the `ItemId` class listing all the available ID types. 40 | 41 | ## Authentication 42 | If you do any request to the Amazon Product Advertising API you have to authenticate yourself. This authentication 43 | consists of an "Associate Tag", an "AWS Access Key" and an "AWS Secret Key". In this library they are grouped inside 44 | a data class called `AmazonWebServiceAuthentication`. This class can easily created using the factory method. 45 | ```java 46 | AmazonWebServiceAuthentication myAuthentication = AmazonWebServiceAuthentication create("myAssociateTag", "myAwsAccessKey", "myAwsSecretKey"); 47 | ``` 48 | These user authentication can be used in multiple requests. 49 | 50 | ## Service Location 51 | Amazon distinguishes its services by location (countries). So you will get different responses for different locations 52 | (e.g. the currency, the available products, ...). You also have to set up your account for every location. 53 | To simplify the selection of the service this library contains an enumeration called `AmazonWebServiceLocation`, which 54 | contains all available service locations. 55 | ```java 56 | // America 57 | AmazonWebServiceLocation.COM 58 | // Germany 59 | AmazonWebServiceLocation.DE 60 | // ... 61 | ``` 62 | 63 | ## Item Lookup 64 | ### Basic Request 65 | If you want to lookup specific item information from Amazon you can do so by creating an Request using the 66 | `AmazonProductAdvertisingApiRequestBuilder`. You create a request builder for an item lookup by giving the `ItemId` 67 | of the item to search for. To create the request url you have to give the `AmazonWebServiceLocation` that shall 68 | be requested and the `AmazonWebServiceAuthentication` that shall be used. 69 | ```java 70 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 71 | .createRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 72 | ``` 73 | The result is a request URL as String, that can be used to do the request with your favourite http request library. The 74 | result of the `createRequestUrlFor()` method will create a http link. If you prefer the https protocol in your url, simply 75 | call the `createSecureRequestUrlFor()`. 76 | ```java 77 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 78 | .createSecureRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 79 | ``` 80 | 81 | ### Specify the included Item Information 82 | You also have the possibility to specify which item information will be returned by the amazon service when calling 83 | the request URL. This can be done using the `ItemInformation` enumeration, which lists all available item information 84 | categories. If you don't pass anything the request builder will add information about the item attributes to the request. 85 | ```java 86 | // static import was used for ItemInformation 87 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 88 | .includeInformationAbout(ATTRIBUTES) 89 | .includeInformationAbout(OFFERS) 90 | .includeInformationAbout(IMAGES) 91 | .createRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 92 | ``` 93 | 94 | ### Filter the Result 95 | If you want to specify the result set, there is the possibility to do so by filtering the results by the condition of 96 | the products. All available conditions are listed inside the `ItemCondition` enumeration. An example usage is shown below. 97 | ```java 98 | // static import was used for ItemInformation and ItemCondition 99 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 100 | .includeInformationAbout(ATTRIBUTES) 101 | .includeInformationAbout(OFFERS) 102 | .includeInformationAbout(IMAGES) 103 | .filterByCondition(NEW) 104 | .createRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 105 | ``` 106 | 107 | ## Item Search 108 | ### Basic Request 109 | To do a basic item search request by keywords to Amazon you can do so by creating an Request using the 110 | `AmazonProductAdvertisingApiRequestBuilder`. You create a request builder for an item lookup by giving the keywords that 111 | shall be searched for as string (spaces are allowed). To create the request url you have to give the again the 112 | `AmazonWebServiceLocation` that shall be requested and the `AmazonWebServiceAuthentication` that shall be used. 113 | ```java 114 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch("Deadpool Movie") 115 | .createRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 116 | ``` 117 | The result is a request URL as String, that can be used to do the request with your favourite http request library. The 118 | result of the `createRequestUrlFor()` method will create a http link. If you prefer the https protocol in your url, simply 119 | call the `createSecureRequestUrlFor()`. 120 | ```java 121 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 122 | .createSecureRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 123 | ``` 124 | 125 | ### Specify the included Item Information 126 | You also have the possibility to specify which item information will be returned by the amazon service when calling 127 | the request URL. This can be done using the `ItemInformation` enumeration, which lists all available item information 128 | categories. If you don't pass anything the request builder will add information about the item attributes to the request. 129 | ```java 130 | // static import was used for ItemInformation 131 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch("Deadpool Movie") 132 | .includeInformationAbout(ATTRIBUTES) 133 | .includeInformationAbout(OFFERS) 134 | .includeInformationAbout(IMAGES) 135 | .createRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 136 | ``` 137 | 138 | ### Filter the Result 139 | If you want to specify the result set, there is the possibility to do so by filtering the results by the condition, the 140 | category, the minimum, as well as the maximum price of the items/products. All available conditions are listed inside 141 | the `ItemCondition` enumeration. The available categories are inside the enumeration called `ItemCategory`. To filter by 142 | minimum and maximum price you have to give an integer representing the price in the smallest currency unit of the given 143 | location. An example usage is shown below. This filters can be used separately as well as in combination. 144 | ```java 145 | // static import was used for ItemInformation and ItemCondition 146 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 147 | .includeInformationAbout(ATTRIBUTES) 148 | .includeInformationAbout(OFFERS) 149 | .includeInformationAbout(IMAGES) 150 | .filterByCondition(NEW) 151 | .filterByCategroy(DVD) 152 | .filterByMinimumPrice(10000) 153 | .filterByMaximumPrice(30000) 154 | .createRequestUrlFor(AmazonWebServiceLocation.COM, authentication); 155 | ``` -------------------------------------------------------------------------------- /README/apaapi_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder/4fdee03703af49b993ce82468959ae85f2f55cd5/README/apaapi_logo.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'de.codecrafters.apaarb' 2 | version '0.9.1' 3 | 4 | apply plugin: 'java' 5 | 6 | sourceCompatibility = 1.7 7 | 8 | ext { 9 | bintrayRepo = 'maven' 10 | bintrayName = 'AmazonProductAdvertisingApiRequestBuilder' 11 | 12 | publishedGroupId = group 13 | libraryName = 'AmazonProductAdvertisingApiRequestBuilder' 14 | artifact = 'apaarb' 15 | 16 | libraryDescription = 'Build Amazon Product Advertising API request URLs without having to read the ' + 17 | 'documentation or study the used hashing algorithms.' 18 | 19 | siteUrl = 'https://github.com/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder' 20 | gitUrl = 'https://github.com/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder.git' 21 | 22 | libraryVersion = version 23 | 24 | developerId = 'ischwarz' 25 | developerName = 'Ingo Schwarz' 26 | developerEmail = 'schwarz-ingo@web.de' 27 | 28 | licenseName = 'The Apache Software License, Version 2.0' 29 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 30 | allLicenses = ["Apache-2.0"] 31 | } 32 | 33 | buildscript { 34 | repositories { 35 | jcenter() 36 | } 37 | dependencies { 38 | classpath 'com.android.tools.build:gradle:2.1.3' 39 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' 40 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 41 | } 42 | } 43 | 44 | repositories { 45 | mavenCentral() 46 | } 47 | 48 | dependencies { 49 | testCompile group: 'junit', name: 'junit', version: '4.11' 50 | } 51 | 52 | // do a 'gradlew install' followed by a 'gradlew bintrayUpload' 53 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 54 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ISchwarz23/Amazon-Product-Advertising-API-URL-Builder/4fdee03703af49b993ce82468959ae85f2f55cd5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 08 15:30:38 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'apaarb' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/AmazonProductAdvertisingApiRequestBuilder.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.*; 5 | 6 | /** 7 | * A builder that simplifies the creation of URLs for requesting the Amazon Product Advertising API. 8 | * 9 | * @author ISchwarz 10 | */ 11 | public final class AmazonProductAdvertisingApiRequestBuilder { 12 | 13 | private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 14 | private static final String DATE_FORMATTER_TIME_ZONE = "GMT"; 15 | private static final String SERVICE = "AWSECommerceService"; 16 | private static final String VERSION = "2011-08-01"; 17 | 18 | private static final String HTTP_PROTOCOL = "http://"; 19 | private static final String HTTPS_PROTOCOL = "https://"; 20 | private static final String ROUTE = "/onca/xml"; 21 | 22 | static { 23 | DATE_FORMATTER.setTimeZone(TimeZone.getTimeZone(DATE_FORMATTER_TIME_ZONE)); 24 | } 25 | 26 | 27 | private AmazonProductAdvertisingApiRequestBuilder() { 28 | //no instance 29 | } 30 | 31 | /** 32 | * Creates an {@link AdvertisingApiItemLookupRequestBuilder} for creating an ItemLookup request for the item with the given ID. 33 | * 34 | * @param itemId The ID of the item to get the information for. 35 | * @param itemIdType The ID type of the given ID. 36 | * @return A new {@link AdvertisingApiItemLookupRequestBuilder} for creating an ItemLookup request for the item with the given ID. 37 | */ 38 | public static AdvertisingApiItemLookupRequestBuilder forItemLookup(final String itemId, final ItemId.Type itemIdType) { 39 | return forItemLookup(ItemId.create(itemId, itemIdType)); 40 | } 41 | 42 | /** 43 | * Creates an {@link AdvertisingApiItemLookupRequestBuilder} for creating an ItemLookup request for the item with the given ID. 44 | * 45 | * @param itemId The ID of the item to get the information for. 46 | * @return A new {@link AdvertisingApiItemLookupRequestBuilder} for creating an ItemLookup request for the item with the given ID. 47 | */ 48 | public static AdvertisingApiItemLookupRequestBuilder forItemLookup(final ItemId itemId) { 49 | return new AdvertisingApiItemLookupRequestBuilder(itemId); 50 | } 51 | 52 | /** 53 | * Creates an {@link AdvertisingApiItemSearchRequestBuilder} for creating an ItemSearch request for the item matching the given keywords. 54 | * 55 | * @param keywords The keywords that shall be used to search for items. 56 | * @return A new {@link AdvertisingApiItemSearchRequestBuilder} for creating an ItemSearch request for the item matching the given keywords. 57 | */ 58 | public static AdvertisingApiItemSearchRequestBuilder forItemSearch(final String keywords) { 59 | return new AdvertisingApiItemSearchRequestBuilder(keywords); 60 | } 61 | 62 | /** 63 | * A builder that simplifies the creation of URLs for ItemSearch requests. 64 | * 65 | * @author ISchwarz 66 | */ 67 | public static final class AdvertisingApiItemSearchRequestBuilder { 68 | 69 | private static final String OPERATION = "ItemSearch"; 70 | 71 | private final List responseGroup = new ArrayList<>(); 72 | private final String keywords; 73 | 74 | private ItemCondition itemCondition = ItemCondition.ALL; 75 | private ItemCategory itemCategory = ItemCategory.ALL; 76 | private int maximumPrice = -1; 77 | private int minimumPrice = -1; 78 | 79 | 80 | private AdvertisingApiItemSearchRequestBuilder(final String keywords) { 81 | this.keywords = keywords; 82 | } 83 | 84 | /** 85 | * Sets the {@link ItemCondition} to filter the result of the returned items. 86 | * 87 | * @param itemCondition The {@link ItemInformation} to filter the result of the returned items. 88 | * @return The current {@link AdvertisingApiItemSearchRequestBuilder}. 89 | */ 90 | public AdvertisingApiItemSearchRequestBuilder filterByCondition(final ItemCondition itemCondition) { 91 | this.itemCondition = itemCondition; 92 | return this; 93 | } 94 | 95 | /** 96 | * Adds the given {@link ItemInformation} to the response group. 97 | * 98 | * @param itemInformation The {@link ItemInformation} that shall be added to the response group. 99 | * @return The current {@link AdvertisingApiItemSearchRequestBuilder}. 100 | */ 101 | public AdvertisingApiItemSearchRequestBuilder includeInformationAbout(final ItemInformation itemInformation) { 102 | responseGroup.add(itemInformation); 103 | return this; 104 | } 105 | 106 | /** 107 | * Specifies the {@link ItemCategory} that will be searched. 108 | * 109 | * @param itemCategory The {@link ItemCategory} that will be searched. 110 | * @return The current {@link AdvertisingApiItemSearchRequestBuilder}. 111 | */ 112 | public AdvertisingApiItemSearchRequestBuilder filterByCategroy(final ItemCategory itemCategory) { 113 | this.itemCategory = itemCategory; 114 | return this; 115 | } 116 | 117 | /** 118 | * Specifies the maximum item price in the response. Prices appear in the lowest currency denomination. 119 | * For example, 3241 is $32.41. 120 | * 121 | * @param maximumPrice The maximum item price in the lowest currency denomination. 122 | * @return The current {@link AdvertisingApiItemSearchRequestBuilder}. 123 | */ 124 | public AdvertisingApiItemSearchRequestBuilder filterByMaximumPrice(final int maximumPrice) { 125 | this.maximumPrice = maximumPrice; 126 | return this; 127 | } 128 | 129 | /** 130 | * Specifies the minimum item price in the response. Prices appear in the lowest currency denomination. 131 | * For example, 3241 is $32.41. 132 | * 133 | * @param minimumPrice The minimum item price in the lowest currency denomination. 134 | * @return The current {@link AdvertisingApiItemSearchRequestBuilder}. 135 | */ 136 | public AdvertisingApiItemSearchRequestBuilder filterByMinimumPrice(final int minimumPrice) { 137 | this.minimumPrice = minimumPrice; 138 | return this; 139 | } 140 | 141 | /** 142 | * Creates the signed request http-url for the given service using the given {@link AmazonWebServiceAuthentication}. 143 | * 144 | * @param serviceLocation The location of the Amazon service that shall be used. 145 | * @param authentication The {@link AmazonWebServiceAuthentication} that shall be used. 146 | * @return The created signed request url. 147 | */ 148 | public String createRequestUrlFor(final AmazonWebServiceLocation serviceLocation, 149 | final AmazonWebServiceAuthentication authentication) { 150 | 151 | return createRequestUrlFor(serviceLocation, authentication, HTTP_PROTOCOL); 152 | } 153 | 154 | /** 155 | * Creates the signed request https-url for the given service using the given {@link AmazonWebServiceAuthentication}. 156 | * 157 | * @param serviceLocation The location of the Amazon service that shall be used. 158 | * @param authentication The {@link AmazonWebServiceAuthentication} that shall be used. 159 | * @return The created signed request url. 160 | */ 161 | public String createSecureRequestUrlFor(final AmazonWebServiceLocation serviceLocation, 162 | final AmazonWebServiceAuthentication authentication) { 163 | 164 | return createRequestUrlFor(serviceLocation, authentication, HTTPS_PROTOCOL); 165 | } 166 | 167 | private String createRequestUrlFor(final AmazonWebServiceLocation serviceLocation, 168 | final AmazonWebServiceAuthentication authentication, final String protocol) { 169 | 170 | final Map requestParams = new LinkedHashMap<>(); 171 | requestParams.put("AWSAccessKeyId", authentication.getAwsAccessKey()); 172 | requestParams.put("AssociateTag", authentication.getAssociateTag()); 173 | requestParams.put("Condition", itemCondition.getRequestValue()); 174 | requestParams.put("Keywords", keywords); 175 | requestParams.put("Operation", OPERATION); 176 | requestParams.put("ResponseGroup", createResponseGroupRequestValue(responseGroup)); 177 | requestParams.put("SearchIndex", itemCategory.getRequestValue()); 178 | requestParams.put("Service", SERVICE); 179 | requestParams.put("Timestamp", DATE_FORMATTER.format(new Date())); 180 | requestParams.put("Version", VERSION); 181 | if (maximumPrice != -1) { 182 | requestParams.put("MaximumPrice", "" + maximumPrice); 183 | } 184 | if (minimumPrice != -1) { 185 | requestParams.put("MinimumPrice", "" + minimumPrice); 186 | } 187 | 188 | return RequestUrlUtils.createSignedRequestUrl(protocol, serviceLocation.getWebServiceUrl(), ROUTE, 189 | requestParams, authentication.getAwsSecretKey()); 190 | } 191 | } 192 | 193 | /** 194 | * A builder that simplifies the creation of URLs for ItemLookup requests. 195 | * 196 | * @author ISchwarz 197 | */ 198 | public static final class AdvertisingApiItemLookupRequestBuilder { 199 | 200 | private static final String OPERATION = "ItemLookup"; 201 | 202 | private final List responseGroup = new ArrayList<>(); 203 | private final ItemId itemId; 204 | 205 | private ItemCondition itemCondition = ItemCondition.ALL; 206 | 207 | 208 | private AdvertisingApiItemLookupRequestBuilder(final ItemId itemId) { 209 | this.itemId = itemId; 210 | } 211 | 212 | /** 213 | * Adds the given {@link ItemInformation} to the response group. 214 | * 215 | * @param itemInformation The {@link ItemInformation} that shall be added to the response group. 216 | * @return The current {@link AdvertisingApiItemLookupRequestBuilder}. 217 | */ 218 | public AdvertisingApiItemLookupRequestBuilder includeInformationAbout(final ItemInformation itemInformation) { 219 | responseGroup.add(itemInformation); 220 | return this; 221 | } 222 | 223 | /** 224 | * Sets the {@link ItemCondition} to filter the result of the returned items. 225 | * 226 | * @param itemCondition The {@link ItemInformation} to filter the result of the returned items. 227 | * @return The current {@link AdvertisingApiItemLookupRequestBuilder}. 228 | */ 229 | public AdvertisingApiItemLookupRequestBuilder filterByCondition(final ItemCondition itemCondition) { 230 | this.itemCondition = itemCondition; 231 | return this; 232 | } 233 | 234 | /** 235 | * Creates the signed request http-url for the given service using the given {@link AmazonWebServiceAuthentication}. 236 | * 237 | * @param serviceLocation The location of the Amazon service that shall be used. 238 | * @param authentication The {@link AmazonWebServiceAuthentication} that shall be used. 239 | * @return The created signed request url. 240 | */ 241 | public String createRequestUrlFor(final AmazonWebServiceLocation serviceLocation, 242 | final AmazonWebServiceAuthentication authentication) { 243 | 244 | return createRequestUrlFor(serviceLocation, authentication, HTTP_PROTOCOL); 245 | } 246 | 247 | /** 248 | * Creates the signed request https-url for the given service using the given {@link AmazonWebServiceAuthentication}. 249 | * 250 | * @param serviceLocation The location of the Amazon service that shall be used. 251 | * @param authentication The {@link AmazonWebServiceAuthentication} that shall be used. 252 | * @return The created signed request url. 253 | */ 254 | public String createSecureRequestUrlFor(final AmazonWebServiceLocation serviceLocation, 255 | final AmazonWebServiceAuthentication authentication) { 256 | 257 | return createRequestUrlFor(serviceLocation, authentication, HTTPS_PROTOCOL); 258 | } 259 | 260 | private String createRequestUrlFor(final AmazonWebServiceLocation serviceLocation, 261 | final AmazonWebServiceAuthentication authentication, final String protocol) { 262 | 263 | final Map requestParams = new LinkedHashMap<>(); 264 | requestParams.put("AWSAccessKeyId", authentication.getAwsAccessKey()); 265 | requestParams.put("AssociateTag", authentication.getAssociateTag()); 266 | requestParams.put("Condition", itemCondition.getRequestValue()); 267 | requestParams.put("IdType", itemId.getType().getRequestValue()); 268 | requestParams.put("ItemId", itemId.getValue()); 269 | requestParams.put("Operation", OPERATION); 270 | requestParams.put("ResponseGroup", createResponseGroupRequestValue(responseGroup)); 271 | requestParams.put("Service", SERVICE); 272 | requestParams.put("Timestamp", DATE_FORMATTER.format(new Date())); 273 | requestParams.put("Version", VERSION); 274 | 275 | return RequestUrlUtils.createSignedRequestUrl(protocol, serviceLocation.getWebServiceUrl(), ROUTE, 276 | requestParams, authentication.getAwsSecretKey()); 277 | } 278 | } 279 | 280 | private static String createResponseGroupRequestValue(final List responseGroup) { 281 | // add item attributes to response group if none was selected 282 | if (responseGroup.size() == 0) { 283 | responseGroup.add(ItemInformation.ATTRIBUTES); 284 | } 285 | 286 | String responseGroupRequestValue = ""; 287 | for (int i = 0; i < responseGroup.size(); i++) { 288 | if (i != 0) { 289 | responseGroupRequestValue += ","; 290 | } 291 | responseGroupRequestValue += responseGroup.get(i).getRequestValue(); 292 | } 293 | return responseGroupRequestValue; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/AmazonWebServiceAuthentication.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | /** 4 | * A value-base data object containing the user information needed for authentication at Amazon services. 5 | * 6 | * @author ISchwarz 7 | */ 8 | public final class AmazonWebServiceAuthentication { 9 | 10 | private final String associateTag; 11 | private final String awsAccessKey; 12 | private final String awsSecretKey; 13 | 14 | private AmazonWebServiceAuthentication(final String associateTag, final String awsAccessKey, final String awsSecretKey) { 15 | this.associateTag = associateTag; 16 | this.awsAccessKey = awsAccessKey; 17 | this.awsSecretKey = awsSecretKey; 18 | } 19 | 20 | /** 21 | * Creates a new {@link AmazonWebServiceAuthentication} with the given values. 22 | * 23 | * @param associateTag The associate tag identifying the user of the Amazon service. 24 | * @param awsAccessKey The access key needed to access a Amazon service. 25 | * @param awsSecretKey The secret key needed to access a Amazon service. 26 | * @return The created {@link AmazonWebServiceAuthentication} containing the given values. 27 | */ 28 | public static AmazonWebServiceAuthentication create(final String associateTag, final String awsAccessKey, final String awsSecretKey) { 29 | return new AmazonWebServiceAuthentication(associateTag, awsAccessKey, awsSecretKey); 30 | } 31 | 32 | /** 33 | * Gives the associate tag identifying the user of the Amazon service. 34 | * 35 | * @return The associate tag identifying the user of the Amazon service. 36 | */ 37 | public String getAssociateTag() { 38 | return associateTag; 39 | } 40 | 41 | /** 42 | * Gives the access key needed to access a Amazon service. 43 | * 44 | * @return The access key needed to access a Amazon service. 45 | */ 46 | public String getAwsAccessKey() { 47 | return awsAccessKey; 48 | } 49 | 50 | /** 51 | * Gives the secret key needed to access a Amazon service. 52 | * 53 | * @return The secret key needed to access a Amazon service. 54 | */ 55 | public String getAwsSecretKey() { 56 | return awsSecretKey; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/AmazonWebServiceLocation.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | /** 4 | * Listing of the available Amazon web services for the different locations. 5 | * 6 | * @author ISchwarz 7 | */ 8 | public enum AmazonWebServiceLocation { 9 | 10 | CA("webservices.amazon.ca"), 11 | CN("webservices.amazon.cn"), 12 | CO_JP("webservices.amazon.co.jp"), 13 | CO_UK("webservices.amazon.co.uk"), 14 | COM("webservices.amazon.com"), 15 | COM_BR("webservices.amazon.com.br"), 16 | COM_MX("webservices.amazon.com.mx"), 17 | DE("webservices.amazon.de"), 18 | ES("webservices.amazon.es"), 19 | FR("webservices.amazon.fr"), 20 | IT("webservices.amazon.it"); 21 | 22 | private final String webServiceUrl; 23 | 24 | AmazonWebServiceLocation(final String webServiceUrl) { 25 | this.webServiceUrl = webServiceUrl; 26 | } 27 | 28 | String getWebServiceUrl() { 29 | return webServiceUrl; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/Base64.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package de.codecrafters.apaarb; 27 | 28 | import java.util.Arrays; 29 | 30 | /** 31 | * An extract of the Base64 Implementation in Java8. As this implementation differs 32 | * in the different java version I was forced to copy an extract of it to this library 33 | * to ensure that the result is always the same (without respecting the java version used.
34 | *

35 | * Original JavaDoc:
36 | * This class consists exclusively of static methods for obtaining 37 | * encoders and decoders for the Base64 encoding scheme. The 38 | * implementation of this class supports the following types of Base64 39 | * as specified in 40 | * RFC 4648 and 41 | * RFC 2045. 42 | *

43 | *

    44 | *
  • Basic 45 | *

    Uses "The Base64 Alphabet" as specified in Table 1 of 46 | * RFC 4648 and RFC 2045 for encoding and decoding operation. 47 | * The encoder does not add any line feed (line separator) 48 | * character. The decoder rejects data that contains characters 49 | * outside the base64 alphabet.

  • 50 | *

    51 | *

  • URL and Filename safe 52 | *

    Uses the "URL and Filename safe Base64 Alphabet" as specified 53 | * in Table 2 of RFC 4648 for encoding and decoding. The 54 | * encoder does not add any line feed (line separator) character. 55 | * The decoder rejects data that contains characters outside the 56 | * base64 alphabet.

  • 57 | *

    58 | *

  • MIME 59 | *

    Uses the "The Base64 Alphabet" as specified in Table 1 of 60 | * RFC 2045 for encoding and decoding operation. The encoded output 61 | * must be represented in lines of no more than 76 characters each 62 | * and uses a carriage return {@code '\r'} followed immediately by 63 | * a linefeed {@code '\n'} as the line separator. No line separator 64 | * is added to the end of the encoded output. All line separators 65 | * or other characters not found in the base64 alphabet table are 66 | * ignored in decoding operation.

  • 67 | *
68 | *

69 | *

Unless otherwise noted, passing a {@code null} argument to a 70 | * method of this class will cause a {@link NullPointerException 71 | * NullPointerException} to be thrown. 72 | * 73 | * @author Xueming Shen 74 | * @since 1.8 75 | */ 76 | 77 | final class Base64 { 78 | 79 | private Base64() { 80 | } 81 | 82 | /** 83 | * Returns a {@link Encoder} that encodes using the 84 | * Basic type base64 encoding scheme. 85 | * 86 | * @return A Base64 encoder. 87 | */ 88 | static Encoder getEncoder() { 89 | return Encoder.RFC4648; 90 | } 91 | 92 | /** 93 | * This class implements an encoder for encoding byte data using 94 | * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045. 95 | *

96 | *

Instances of {@link Encoder} class are safe for use by 97 | * multiple concurrent threads. 98 | *

99 | *

Unless otherwise noted, passing a {@code null} argument to 100 | * a method of this class will cause a 101 | * {@link NullPointerException NullPointerException} to 102 | * be thrown. 103 | * 104 | * @see Decoder 105 | * @since 1.8 106 | */ 107 | static class Encoder { 108 | 109 | private final byte[] newline; 110 | private final int linemax; 111 | private final boolean isURL; 112 | private final boolean doPadding; 113 | 114 | private Encoder(final boolean isURL, final byte[] newline, final int linemax, final boolean doPadding) { 115 | this.isURL = isURL; 116 | this.newline = newline; 117 | this.linemax = linemax; 118 | this.doPadding = doPadding; 119 | } 120 | 121 | /** 122 | * This array is a lookup table that translates 6-bit positive integer 123 | * index values into their "Base64 Alphabet" equivalents as specified 124 | * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648). 125 | */ 126 | private static final char[] toBase64 = { 127 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 128 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 129 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 130 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 131 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' 132 | }; 133 | 134 | /** 135 | * It's the lookup table for "URL and Filename safe Base64" as specified 136 | * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and 137 | * '_'. This table is used when BASE64_URL is specified. 138 | */ 139 | private static final char[] toBase64URL = { 140 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 141 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 142 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 143 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 144 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' 145 | }; 146 | 147 | private static final int MIMELINEMAX = 76; 148 | 149 | static final Encoder RFC4648 = new Encoder(false, null, -1, true); 150 | 151 | private int outLength(final int srclen) { 152 | int len; 153 | if (doPadding) { 154 | len = 4 * ((srclen + 2) / 3); 155 | } else { 156 | final int n = srclen % 3; 157 | len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1); 158 | } 159 | if (linemax > 0) // line separators 160 | len += (len - 1) / linemax * newline.length; 161 | return len; 162 | } 163 | 164 | /** 165 | * Encodes all bytes from the specified byte array into a newly-allocated 166 | * byte array using the {@link Base64} encoding scheme. The returned byte 167 | * array is of the length of the resulting bytes. 168 | * 169 | * @param src the byte array to encode 170 | * @return A newly-allocated byte array containing the resulting 171 | * encoded bytes. 172 | */ 173 | byte[] encode(final byte[] src) { 174 | final int len = outLength(src.length); // dst array size 175 | final byte[] dst = new byte[len]; 176 | final int ret = encode0(src, 0, src.length, dst); 177 | if (ret != dst.length) 178 | return Arrays.copyOf(dst, ret); 179 | return dst; 180 | } 181 | 182 | private int encode0(final byte[] src, final int off, final int end, final byte[] dst) { 183 | final char[] base64 = isURL ? toBase64URL : toBase64; 184 | int sp = off; 185 | int slen = (end - off) / 3 * 3; 186 | final int sl = off + slen; 187 | if (linemax > 0 && slen > linemax / 4 * 3) 188 | slen = linemax / 4 * 3; 189 | int dp = 0; 190 | while (sp < sl) { 191 | final int sl0 = Math.min(sp + slen, sl); 192 | for (int sp0 = sp, dp0 = dp; sp0 < sl0; ) { 193 | final int bits = (src[sp0++] & 0xff) << 16 | 194 | (src[sp0++] & 0xff) << 8 | 195 | (src[sp0++] & 0xff); 196 | dst[dp0++] = (byte) base64[(bits >>> 18) & 0x3f]; 197 | dst[dp0++] = (byte) base64[(bits >>> 12) & 0x3f]; 198 | dst[dp0++] = (byte) base64[(bits >>> 6) & 0x3f]; 199 | dst[dp0++] = (byte) base64[bits & 0x3f]; 200 | } 201 | final int dlen = (sl0 - sp) / 3 * 4; 202 | dp += dlen; 203 | sp = sl0; 204 | if (dlen == linemax && sp < end) { 205 | for (final byte b : newline) { 206 | dst[dp++] = b; 207 | } 208 | } 209 | } 210 | if (sp < end) { // 1 or 2 leftover bytes 211 | final int b0 = src[sp++] & 0xff; 212 | dst[dp++] = (byte) base64[b0 >> 2]; 213 | if (sp == end) { 214 | dst[dp++] = (byte) base64[(b0 << 4) & 0x3f]; 215 | if (doPadding) { 216 | dst[dp++] = '='; 217 | dst[dp++] = '='; 218 | } 219 | } else { 220 | final int b1 = src[sp++] & 0xff; 221 | dst[dp++] = (byte) base64[(b0 << 4) & 0x3f | (b1 >> 4)]; 222 | dst[dp++] = (byte) base64[(b1 << 2) & 0x3f]; 223 | if (doPadding) { 224 | dst[dp++] = '='; 225 | } 226 | } 227 | } 228 | return dp; 229 | } 230 | } 231 | 232 | /** 233 | * This class implements a decoder for decoding byte data using the 234 | * Base64 encoding scheme as specified in RFC 4648 and RFC 2045. 235 | *

236 | *

The Base64 padding character {@code '='} is accepted and 237 | * interpreted as the end of the encoded byte data, but is not 238 | * required. So if the final unit of the encoded byte data only has 239 | * two or three Base64 characters (without the corresponding padding 240 | * character(s) padded), they are decoded as if followed by padding 241 | * character(s). If there is a padding character present in the 242 | * final unit, the correct number of padding character(s) must be 243 | * present, otherwise {@code IllegalArgumentException} ( 244 | * {@code IOException} when reading from a Base64 stream) is thrown 245 | * during decoding. 246 | *

247 | *

Instances of {@link Decoder} class are safe for use by 248 | * multiple concurrent threads. 249 | *

250 | *

Unless otherwise noted, passing a {@code null} argument to 251 | * a method of this class will cause a 252 | * {@link NullPointerException NullPointerException} to 253 | * be thrown. 254 | * 255 | * @see Encoder 256 | * @since 1.8 257 | */ 258 | private static class Decoder { 259 | 260 | private final boolean isURL; 261 | private final boolean isMIME; 262 | 263 | private Decoder(final boolean isURL, final boolean isMIME) { 264 | this.isURL = isURL; 265 | this.isMIME = isMIME; 266 | } 267 | 268 | /** 269 | * Lookup table for decoding unicode characters drawn from the 270 | * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into 271 | * their 6-bit positive integer equivalents. Characters that 272 | * are not in the Base64 alphabet but fall within the bounds of 273 | * the array are encoded to -1. 274 | */ 275 | private static final int[] fromBase64 = new int[256]; 276 | 277 | static { 278 | Arrays.fill(fromBase64, -1); 279 | for (int i = 0; i < Encoder.toBase64.length; i++) 280 | fromBase64[Encoder.toBase64[i]] = i; 281 | fromBase64['='] = -2; 282 | } 283 | 284 | /** 285 | * Lookup table for decoding "URL and Filename safe Base64 Alphabet" 286 | * as specified in Table2 of the RFC 4648. 287 | */ 288 | private static final int[] fromBase64URL = new int[256]; 289 | 290 | static { 291 | Arrays.fill(fromBase64URL, -1); 292 | for (int i = 0; i < Encoder.toBase64URL.length; i++) 293 | fromBase64URL[Encoder.toBase64URL[i]] = i; 294 | fromBase64URL['='] = -2; 295 | } 296 | 297 | } 298 | } -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/ItemCategory.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | /** 4 | * Listing of all Item Categories managed by Amazon. 5 | * 6 | * @author ISchwarz 7 | */ 8 | public enum ItemCategory { 9 | 10 | ALL("All"), 11 | APPAREL("Apparel"), 12 | AUTOMOTIVE("Automotive"), 13 | BABY("Baby"), 14 | BEAUTY("Beauty"), 15 | BOOKS("Books"), 16 | CLASSICAL("Classical"), 17 | DVD("DVD"), 18 | ELECTRONICS("Electronics"), 19 | FOREIGN_BOOKS("ForeignBooks"), 20 | GROCERY("Grocery"), 21 | HEALTH_AND_PERSONAL_CARE("HealthPersonalCare"), 22 | HOME_AND_GARDEN("HomeGarden"), 23 | JEWELRY("Jewelry"), 24 | KINDLE_STORE("KindleStore"), 25 | KITCHEN("Kitchen"), 26 | LIGHTING("Lighting"), 27 | MAGAZINES("Magazines"), 28 | MARKETPLACE("Marketplace"), 29 | MP3_DOWNLOADS("MP3Downloads"), 30 | MUSIC("Music"), 31 | MUSICAL_INSTRUMENTS("MusicalInstruments"), 32 | MUSIC_TRACKS("MusicTracks"), 33 | OFFICE_PRODUCTS("OfficeProducts"), 34 | OUTDOOR_LIVING("OutdoorLiving"), 35 | OUTLET("Outlet"), 36 | PC_HARDWARE("PCHardware"), 37 | PHOTO("Photo"), 38 | SHOES("Shoes"), 39 | SOFTWARE("Software"), 40 | SOFTWARE_VIDEO_GAMES("SoftwareVideoGames"), 41 | SPORTING_GOODS("SportingGoods"), 42 | TOOLS("Tools"), 43 | TOYS("Toys"), 44 | VHS("VHS"), 45 | VIDEO("Video"), 46 | VIDEO_GAMES("VideoGames"), 47 | WATCHES("Watches"); 48 | 49 | private final String requestValue; 50 | 51 | ItemCategory(final String requestValue) { 52 | this.requestValue = requestValue; 53 | } 54 | 55 | public String getRequestValue() { 56 | return requestValue; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/ItemCondition.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | /** 4 | * A listing that contains all item conditions available at Amazon. 5 | * 6 | * @author ISchwarz 7 | */ 8 | public enum ItemCondition { 9 | 10 | ALL("All"), 11 | NEW("New"), 12 | USED("Used"), 13 | REFURBISHED("Refurbished"), 14 | COLLECTIBLE("Collectible"); 15 | 16 | private final String requestValue; 17 | 18 | ItemCondition(final String requestValue) { 19 | this.requestValue = requestValue; 20 | } 21 | 22 | /** 23 | * Gives the value used in the URL request that represents this item condition. 24 | * 25 | * @return The value used in the URL request that represents this item condition. 26 | */ 27 | String getRequestValue() { 28 | return requestValue; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/ItemId.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | /** 4 | * A value-based data class representing the ID of an Amazon item. It supports all ID-types available at Amazon. 5 | * 6 | * @author ISchwarz 7 | */ 8 | public final class ItemId { 9 | 10 | /** 11 | * The {@link ItemId.Type} contains all available IDs managed by amazon. 12 | * 13 | * @author ISchwarz 14 | */ 15 | public enum Type { 16 | 17 | ASIN("ASIN"), 18 | ISBN("ISBN"), 19 | UPC("UPC"), 20 | EAN("EAN"); 21 | 22 | private final String requestValue; 23 | 24 | Type(final String requestValue) { 25 | this.requestValue = requestValue; 26 | } 27 | 28 | /** 29 | * Gives the value used in the URL request that represents this ID type. 30 | * 31 | * @return The value used in the URL request that represents this ID type. 32 | */ 33 | String getRequestValue() { 34 | return requestValue; 35 | } 36 | } 37 | 38 | private final String value; 39 | private final Type type; 40 | 41 | private ItemId(final String value, final Type type) { 42 | this.value = value; 43 | this.type = type; 44 | } 45 | 46 | /** 47 | * Creates an {@link ItemId} of type ASIN with the given value. 48 | * 49 | * @param asinValue The ASIN value of the {@link ItemId}. 50 | * @return The created {@link ItemId} of type ASIN. 51 | */ 52 | public static ItemId createAsin(final String asinValue) { 53 | return create(asinValue, Type.ASIN); 54 | } 55 | 56 | /** 57 | * Creates an {@link ItemId} of type EAN with the given value. 58 | * 59 | * @param eanValue The EAN value of the {@link ItemId}. 60 | * @return The created {@link ItemId} of type EAN. 61 | */ 62 | public static ItemId createEan(final String eanValue) { 63 | return create(eanValue, Type.EAN); 64 | } 65 | 66 | /** 67 | * Creates an {@link ItemId} of type ISBN with the given value. 68 | * 69 | * @param isbnValue The ISBN value of the {@link ItemId}. 70 | * @return The created {@link ItemId} of type ISBN. 71 | */ 72 | public static ItemId createIsbn(final String isbnValue) { 73 | return create(isbnValue, Type.ISBN); 74 | } 75 | 76 | /** 77 | * Creates an {@link ItemId} of type UPC with the given value. 78 | * 79 | * @param upcValue The UPC value of the {@link ItemId}. 80 | * @return The created {@link ItemId} of type UPC. 81 | */ 82 | public static ItemId createUpc(final String upcValue) { 83 | return create(upcValue, Type.UPC); 84 | } 85 | 86 | /** 87 | * Creates an {@link ItemId} of given type with the given value. 88 | * 89 | * @param idValue The value of the {@link ItemId}. 90 | * @param idType The type of the {@link ItemId}. 91 | * @return The created {@link ItemId} of the given type. 92 | */ 93 | static ItemId create(final String idValue, final Type idType) { 94 | return new ItemId(idValue, idType); 95 | } 96 | 97 | /** 98 | * Gives the value of this {@link ItemId}. 99 | * 100 | * @return The value of this {@link ItemId}. 101 | */ 102 | public String getValue() { 103 | return value; 104 | } 105 | 106 | /** 107 | * Gives the type of the {@link ItemId}. 108 | * 109 | * @return The type of the {@link ItemId}. 110 | */ 111 | public Type getType() { 112 | return type; 113 | } 114 | 115 | @Override 116 | public boolean equals(final Object o) { 117 | if (this == o) return true; 118 | if (o == null || getClass() != o.getClass()) return false; 119 | final ItemId id = (ItemId) o; 120 | return value.equals(id.value) && type == id.type; 121 | } 122 | 123 | @Override 124 | public int hashCode() { 125 | int result = value.hashCode(); 126 | result = 31 * result + type.hashCode(); 127 | return result; 128 | } 129 | 130 | @Override 131 | public String toString() { 132 | return "AmazonItemId{" + 133 | "value='" + value + '\'' + 134 | ", type=" + type + 135 | '}'; 136 | } 137 | 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/ItemInformation.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | /** 4 | * A listing that contains all item information available at Amazon. 5 | * 6 | * @author ISchwarz 7 | */ 8 | public enum ItemInformation { 9 | 10 | ACCESSORIES("Accessories"), 11 | ALTERNATIVE_VERSIONS("AlternativeVersions"), 12 | ATTRIBUTES("ItemAttributes"), 13 | BROWSE_NODES("BrowseNodes"), 14 | EDITORIAL_REVIEW("EditorialReview"), 15 | IMAGES("Images"), 16 | OFFER_FULL("OfferFull"), 17 | OFFER_SUMMARY("OfferSummary"), 18 | OFFERS("Offers"), 19 | REVIEWS("Reviews"), 20 | SALES_RANK("SalesRank"), 21 | SIMILARITIES("Similarities"), 22 | TRACKS("Tracks"), 23 | VARIATION_IMAGES("VariationImages"), 24 | VARIATION_MATRIX("VariationMatrix"), 25 | VARIATION_SUMMARY("VariationSummary"), 26 | VARIATIONS("Variations"); 27 | 28 | private final String requestValue; 29 | 30 | ItemInformation(final String requestValue) { 31 | this.requestValue = requestValue; 32 | } 33 | 34 | /** 35 | * Gives the value used in the URL request that represents this item information. 36 | * 37 | * @return The value used in the URL request that represents this item information. 38 | */ 39 | String getRequestValue() { 40 | return requestValue; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/de/codecrafters/apaarb/RequestUrlUtils.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | import javax.crypto.Mac; 4 | import javax.crypto.spec.SecretKeySpec; 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLEncoder; 7 | import java.security.InvalidKeyException; 8 | import java.security.NoSuchAlgorithmException; 9 | import java.util.Iterator; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | 13 | /** 14 | * Utility class to create the hashed 15 | */ 16 | final class RequestUrlUtils { 17 | 18 | private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256"; 19 | private static final String UTF8_CHARSET = "UTF-8"; 20 | 21 | 22 | private RequestUrlUtils() { 23 | //no instance 24 | } 25 | 26 | static String createSignedRequestUrl(final String protocol, final String amazonServiceUrl, final String route, 27 | final Map requestParams, final String secretKey) { 28 | 29 | final String canonicalizeRequestParams = RequestUrlUtils.canonicalizeParameters(requestParams); 30 | final String signature = RequestUrlUtils.createSignature(amazonServiceUrl, route, canonicalizeRequestParams, secretKey); 31 | return protocol + amazonServiceUrl + route + "?" + canonicalizeRequestParams + "&Signature=" + signature; 32 | } 33 | 34 | static String createSignature(final String amazonServiceUrl, final String route, 35 | final String requestPairs, final String secretKey) { 36 | 37 | // create the string upon which the signature is calculated 38 | final String toSign = "GET" + "\n" 39 | + amazonServiceUrl + "\n" 40 | + route + "\n" 41 | + requestPairs; 42 | 43 | // get the signature 44 | final String hmac = hmac(toSign, secretKey); 45 | return percentEncodeRfc3986(hmac); 46 | } 47 | 48 | static String hmac(final String stringToSign, final String awsSecretKey) { 49 | String signature = null; 50 | final byte[] data; 51 | final byte[] rawHmac; 52 | try { 53 | data = stringToSign.getBytes(UTF8_CHARSET); 54 | final Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM); 55 | final byte[] secretKeyBytes = awsSecretKey.getBytes(UTF8_CHARSET); 56 | final SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, HMAC_SHA256_ALGORITHM); 57 | mac.init(secretKeySpec); 58 | rawHmac = mac.doFinal(data); 59 | final Base64.Encoder encoder = Base64.getEncoder(); 60 | signature = new String(encoder.encode(rawHmac)); 61 | } catch (final UnsupportedEncodingException e) { 62 | throw new RuntimeException(UTF8_CHARSET + " is not supported!", e); 63 | } catch (final NoSuchAlgorithmException e) { 64 | throw new RuntimeException(HMAC_SHA256_ALGORITHM + " is not supported!", e); 65 | } catch (final InvalidKeyException e) { 66 | throw new RuntimeException("Private Key is Invalid!", e); 67 | } 68 | return signature; 69 | } 70 | 71 | static String canonicalizeParameters(final Map parameters) { 72 | // The parameters need to be processed in lexicographical order, so we'll 73 | // use a TreeMap implementation for that. 74 | final TreeMap sortedParamMap = new TreeMap<>(parameters); 75 | 76 | if (sortedParamMap.isEmpty()) { 77 | return ""; 78 | } 79 | 80 | final StringBuilder buffer = new StringBuilder(); 81 | final Iterator> paramIterator = sortedParamMap.entrySet().iterator(); 82 | 83 | while (paramIterator.hasNext()) { 84 | final Map.Entry keyValuePair = paramIterator.next(); 85 | buffer.append(percentEncodeRfc3986(keyValuePair.getKey())); 86 | buffer.append("="); 87 | buffer.append(percentEncodeRfc3986(keyValuePair.getValue())); 88 | if (paramIterator.hasNext()) { 89 | buffer.append("&"); 90 | } 91 | } 92 | return buffer.toString(); 93 | } 94 | 95 | static String percentEncodeRfc3986(final String s) { 96 | String out; 97 | try { 98 | out = URLEncoder.encode(s, UTF8_CHARSET) 99 | .replace("+", "%20") 100 | .replace("*", "%2A") 101 | .replace("%7E", "~"); 102 | } catch (final UnsupportedEncodingException e) { 103 | out = s; 104 | } 105 | return out; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/de/codecrafters/apaarb/ItemLookupTest.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import static de.codecrafters.apaarb.ItemCondition.NEW; 7 | import static de.codecrafters.apaarb.ItemInformation.ATTRIBUTES; 8 | import static de.codecrafters.apaarb.ItemInformation.OFFERS; 9 | import static org.hamcrest.CoreMatchers.containsString; 10 | import static org.hamcrest.CoreMatchers.startsWith; 11 | import static org.junit.Assert.assertThat; 12 | 13 | /** 14 | * Test and usage example of the ItemLookup request url builder using the {@link AmazonProductAdvertisingApiRequestBuilder}. 15 | * 16 | * @author ISchwarz 17 | */ 18 | public class ItemLookupTest { 19 | 20 | private static final String ASSOCIATE_TAG = "AssociateTag"; 21 | private static final String AWS_ACCESS_KEY = "AccessKey"; 22 | private static final String AWS_SECRET_KEY = "SecretKey"; 23 | 24 | private static final ItemId ITEM_ID = ItemId.createAsin("B01BKISLYC"); 25 | private AmazonWebServiceAuthentication authentication; 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | authentication = AmazonWebServiceAuthentication.create(ASSOCIATE_TAG, AWS_ACCESS_KEY, AWS_SECRET_KEY); 30 | } 31 | 32 | @Test 33 | public void shouldCreateRequestUrl() throws Exception { 34 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 35 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 36 | 37 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes"); 38 | } 39 | 40 | @Test 41 | public void shouldCreateRequestUrlWithConditionFilter() throws Exception { 42 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 43 | .filterByCondition(NEW) 44 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 45 | 46 | checkRequestUrl(requestUrl, "http", "amazon.de", "New", "ItemAttributes"); 47 | } 48 | 49 | @Test 50 | public void shouldCreateRequestUrlWithSpecificInformation() throws Exception { 51 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 52 | .includeInformationAbout(ATTRIBUTES) 53 | .includeInformationAbout(OFFERS) 54 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 55 | 56 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes%2COffers"); 57 | } 58 | 59 | @Test 60 | public void shouldCreateRequestUrlWithConditionFilterAndSpecificInformation() throws Exception { 61 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 62 | .filterByCondition(NEW) 63 | .includeInformationAbout(ATTRIBUTES) 64 | .includeInformationAbout(OFFERS) 65 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 66 | 67 | checkRequestUrl(requestUrl, "http", "amazon.de", "New", "ItemAttributes%2COffers"); 68 | } 69 | 70 | @Test 71 | public void shouldCreateSecureRequestUrl() throws Exception { 72 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 73 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 74 | 75 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes"); 76 | } 77 | 78 | @Test 79 | public void shouldCreateSecureRequestUrlWithConditionFilter() throws Exception { 80 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 81 | .filterByCondition(NEW) 82 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 83 | 84 | checkRequestUrl(requestUrl, "https", "amazon.de", "New", "ItemAttributes"); 85 | } 86 | 87 | @Test 88 | public void shouldCreateSecureRequestUrlWithSpecificInformation() throws Exception { 89 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 90 | .includeInformationAbout(ATTRIBUTES) 91 | .includeInformationAbout(OFFERS) 92 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 93 | 94 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes%2COffers"); 95 | } 96 | 97 | @Test 98 | public void shouldCreateSecureRequestUrlWithConditionFilterAndSpecificInformation() throws Exception { 99 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemLookup(ITEM_ID) 100 | .filterByCondition(NEW) 101 | .includeInformationAbout(ATTRIBUTES) 102 | .includeInformationAbout(OFFERS) 103 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 104 | 105 | checkRequestUrl(requestUrl, "https", "amazon.de", "New", "ItemAttributes%2COffers"); 106 | } 107 | 108 | private static void checkRequestUrl(final String requestUrl, final String protocol, final String domain, 109 | final String condition, final String responseGroup) { 110 | 111 | assertThat(requestUrl, startsWith(protocol + "://")); 112 | assertThat(requestUrl, containsString(domain)); 113 | assertThat(requestUrl, containsString("AWSAccessKeyId=" + AWS_ACCESS_KEY)); 114 | assertThat(requestUrl, containsString("AssociateTag=" + ASSOCIATE_TAG)); 115 | assertThat(requestUrl, containsString("Condition=" + condition)); 116 | assertThat(requestUrl, containsString("ItemId=" + ITEM_ID.getValue())); 117 | assertThat(requestUrl, containsString("IdType=" + ITEM_ID.getType())); 118 | assertThat(requestUrl, containsString("Operation=ItemLookup")); 119 | assertThat(requestUrl, containsString("ResponseGroup=" + responseGroup)); 120 | assertThat(requestUrl, containsString("Service=AWSECommerceService")); 121 | assertThat(requestUrl, containsString("Timestamp=")); 122 | assertThat(requestUrl, containsString("Version=2011-08-01")); 123 | assertThat(requestUrl, containsString("Signature=")); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/de/codecrafters/apaarb/ItemSearchTest.java: -------------------------------------------------------------------------------- 1 | package de.codecrafters.apaarb; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import static de.codecrafters.apaarb.ItemCategory.HEALTH_AND_PERSONAL_CARE; 7 | import static de.codecrafters.apaarb.ItemCondition.USED; 8 | import static de.codecrafters.apaarb.ItemInformation.ATTRIBUTES; 9 | import static de.codecrafters.apaarb.ItemInformation.OFFERS; 10 | import static org.hamcrest.CoreMatchers.containsString; 11 | import static org.hamcrest.CoreMatchers.startsWith; 12 | import static org.junit.Assert.assertThat; 13 | 14 | /** 15 | * Test and usage example of the ItemSearch request url builder using the {@link AmazonProductAdvertisingApiRequestBuilder}. 16 | * 17 | * @author ISchwarz 18 | */ 19 | public class ItemSearchTest { 20 | 21 | private static final String ASSOCIATE_TAG = "AssociateTag"; 22 | private static final String AWS_ACCESS_KEY = "AccessKey"; 23 | private static final String AWS_SECRET_KEY = "SecretKey"; 24 | 25 | private static final String KEYWORDS = "Dead Pool"; 26 | private AmazonWebServiceAuthentication authentication; 27 | 28 | @Before 29 | public void setUp() throws Exception { 30 | authentication = AmazonWebServiceAuthentication.create(ASSOCIATE_TAG, AWS_ACCESS_KEY, AWS_SECRET_KEY); 31 | } 32 | 33 | @Test 34 | public void shouldCreateRequestUrl() throws Exception { 35 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 36 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 37 | 38 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes", "All"); 39 | } 40 | 41 | @Test 42 | public void shouldCreateRequestUrlWithConditionFilter() throws Exception { 43 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 44 | .filterByCondition(USED) 45 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 46 | 47 | checkRequestUrl(requestUrl, "http", "amazon.de", "Used", "ItemAttributes", "All"); 48 | } 49 | 50 | @Test 51 | public void shouldCreateRequestUrlWithIndexFilter() throws Exception { 52 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 53 | .filterByCategroy(HEALTH_AND_PERSONAL_CARE) 54 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 55 | 56 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes", "HealthPersonalCare"); 57 | } 58 | 59 | @Test 60 | public void shouldCreateRequestUrlWithSpecificInformation() throws Exception { 61 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 62 | .includeInformationAbout(ATTRIBUTES) 63 | .includeInformationAbout(OFFERS) 64 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 65 | 66 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes%2COffers", "All"); 67 | } 68 | 69 | @Test 70 | public void shouldCreateRequestUrlWithMinimumPriceFilter() throws Exception { 71 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 72 | .filterByMinimumPrice(10000) 73 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 74 | 75 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes", "All"); 76 | assertThat(requestUrl, containsString("MinimumPrice=10000")); 77 | } 78 | 79 | @Test 80 | public void shouldCreateRequestUrlWithMaximumPriceFilter() throws Exception { 81 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 82 | .filterByMaximumPrice(10000) 83 | .createRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 84 | 85 | checkRequestUrl(requestUrl, "http", "amazon.de", "All", "ItemAttributes", "All"); 86 | assertThat(requestUrl, containsString("MaximumPrice=10000")); 87 | } 88 | 89 | @Test 90 | public void shouldCreateSecureRequestUrl() throws Exception { 91 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 92 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 93 | 94 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes", "All"); 95 | } 96 | 97 | @Test 98 | public void shouldCreateSecureRequestUrlWithConditionFilter() throws Exception { 99 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 100 | .filterByCondition(USED) 101 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 102 | 103 | checkRequestUrl(requestUrl, "https", "amazon.de", "Used", "ItemAttributes", "All"); 104 | } 105 | 106 | @Test 107 | public void shouldCreateSecureRequestUrlWithIndexFilter() throws Exception { 108 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 109 | .filterByCategroy(HEALTH_AND_PERSONAL_CARE) 110 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 111 | 112 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes", "HealthPersonalCare"); 113 | } 114 | 115 | @Test 116 | public void shouldCreateSecureRequestUrlWithSpecificInformation() throws Exception { 117 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 118 | .includeInformationAbout(ATTRIBUTES) 119 | .includeInformationAbout(OFFERS) 120 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 121 | 122 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes%2COffers", "All"); 123 | } 124 | 125 | @Test 126 | public void shouldCreateSecureRequestUrlWithMinimumPriceFilter() throws Exception { 127 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 128 | .filterByMinimumPrice(10000) 129 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 130 | 131 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes", "All"); 132 | assertThat(requestUrl, containsString("MinimumPrice=10000")); 133 | } 134 | 135 | @Test 136 | public void shouldCreateSecureRequestUrlWithMaximumPriceFilter() throws Exception { 137 | final String requestUrl = AmazonProductAdvertisingApiRequestBuilder.forItemSearch(KEYWORDS) 138 | .filterByMaximumPrice(10000) 139 | .createSecureRequestUrlFor(AmazonWebServiceLocation.DE, authentication); 140 | 141 | checkRequestUrl(requestUrl, "https", "amazon.de", "All", "ItemAttributes", "All"); 142 | assertThat(requestUrl, containsString("MaximumPrice=10000")); 143 | } 144 | 145 | private static void checkRequestUrl(final String requestUrl, final String protocol, final String domain, 146 | final String condition, final String responseGroup, final String searchIndex) { 147 | 148 | assertThat(requestUrl, startsWith(protocol + "://")); 149 | assertThat(requestUrl, containsString(domain)); 150 | assertThat(requestUrl, containsString("AWSAccessKeyId=" + AWS_ACCESS_KEY)); 151 | assertThat(requestUrl, containsString("AssociateTag=" + ASSOCIATE_TAG)); 152 | assertThat(requestUrl, containsString("Condition=" + condition)); 153 | assertThat(requestUrl, containsString("Keywords=" + KEYWORDS.replaceAll(" ", "%20"))); 154 | assertThat(requestUrl, containsString("Operation=ItemSearch")); 155 | assertThat(requestUrl, containsString("ResponseGroup=" + responseGroup)); 156 | assertThat(requestUrl, containsString("SearchIndex=" + searchIndex)); 157 | assertThat(requestUrl, containsString("Service=AWSECommerceService")); 158 | assertThat(requestUrl, containsString("Timestamp=")); 159 | assertThat(requestUrl, containsString("Version=2011-08-01")); 160 | assertThat(requestUrl, containsString("Signature=")); 161 | } 162 | } 163 | --------------------------------------------------------------------------------