From 2f93f0635788a6838882951ceebac516fe281107 Mon Sep 17 00:00:00 2001 From: evdeg Date: Wed, 5 Aug 2020 17:53:39 -0700 Subject: [PATCH] Added the clients directory --- README.md | 1 - .../sellingpartner-api-aa-csharp/.gitignore | 10 + .../sellingpartner-api-aa-csharp/README.md | 96 ++ .../SellingPartnerAPIAuthAndAuthCSharp.sln | 23 + .../AWSAuthenticationCredentials.cs | 20 + .../AWSSigV4Signer.cs | 82 ++ .../AWSSignerHelper.cs | 251 ++++ .../Amazon.SellingPartnerAPIAA.csproj | 16 + .../Amazon.SellingPartnerAPIAA/IDateHelper.cs | 8 + .../LWAAccessTokenRequestMeta.cs | 35 + .../LWAAccessTokenRequestMetaBuilder.cs | 40 + .../LWAAuthorizationCredentials.cs | 39 + .../LWAAuthorizationSigner.cs | 34 + .../Amazon.SellingPartnerAPIAA/LWAClient.cs | 68 + .../ScopeConstants.cs | 8 + .../SigningDateHelper.cs | 11 + .../src/Amazon.SellingPartnerAPIAA/Utils.cs | 73 + .../templates/ApiClient.mustache | 576 ++++++++ .../templates/Configuration.mustache | 464 ++++++ .../templates/IReadableConfiguration.mustache | 98 ++ .../swagger-codegen/templates/api.mustache | 478 +++++++ .../templates/api_test.mustache | 76 + .../AWSSigV4SignerTest.cs | 88 ++ .../AWSSignerHelperTest.cs | 269 ++++ .../Amazon.SellingPartnerAPIAATests.csproj | 20 + .../LWAAccessTokenRequestMetaBuilderTest.cs | 71 + .../LWAAuthorizationSignerTest.cs | 49 + .../LWAClientTest.cs | 148 ++ .../UtilsTest.cs | 55 + clients/sellingpartner-api-aa-java/.gitignore | 9 + clients/sellingpartner-api-aa-java/README.md | 90 ++ clients/sellingpartner-api-aa-java/pom.xml | 113 ++ .../templates/ApiClient.mustache | 1258 +++++++++++++++++ .../templates/StringUtil.mustache | 54 + .../swagger-codegen/templates/api.mustache | 317 +++++ .../AWSAuthenticationCredentials.java | 30 + .../SellingPartnerAPIAA/AWSSigV4Signer.java | 48 + .../LWAAccessTokenRequestMeta.java | 25 + .../LWAAuthorizationCredentials.java | 65 + .../LWAAuthorizationSigner.java | 55 + .../amazon/SellingPartnerAPIAA/LWAClient.java | 58 + .../SellingPartnerAPIAA/LWAClientScopes.java | 27 + ...LWAClientScopesSerializerDeserializer.java | 34 + .../SellingPartnerAPIAA/ScopeConstants.java | 11 + .../SignableRequestImpl.java | 153 ++ .../AWSSigV4SignerTest.java | 83 ++ .../LWAAuthorizationSignerTest.java | 131 ++ ...lientScopesSerializerDeserializerTest.java | 78 + .../SellingPartnerAPIAA/LWAClientTest.java | 185 +++ .../SignableRequestImplTest.java | 236 ++++ .../.gitignore | 11 + .../README.md | 116 ++ .../build.gradle | 107 ++ .../libs/sellingpartner-api-aa-java-1.0.jar | Bin 0 -> 21917 bytes .../libs/sellingpartnerapi-feeds-java-1.0.jar | Bin 0 -> 103341 bytes .../sellingpartnerapi-reports-java-1.0.jar | Bin 0 -> 158402 bytes .../sellingpartnerapi-uploads-java-1.0.jar | Bin 0 -> 65528 bytes .../pom.xml | 164 +++ .../settings.gradle | 1 + .../client/DocumentValidatorInputStream.java | 64 + .../sequencing/client/DownloadsSequencer.java | 23 + .../SpoolingLimitExceededException.java | 19 + .../sequencing/client/UploadDetails.java | 19 + .../sequencing/client/UploadsSequencer.java | 46 + .../client/ValidationFailureException.java | 38 + .../client/impl/AccessMechanism.java | 15 + .../client/impl/DownloadsSequencerImpl.java | 307 ++++ .../client/impl/HttpClientFactory.java | 19 + ...PIUploadDestinationsHttpClientFactory.java | 71 + .../client/impl/SequencerHelper.java | 156 ++ .../client/impl/UploadsSequencerImpl.java | 178 +++ .../client/util/DemuxOutputStream.java | 93 ++ .../client/util/TempInputStream.java | 133 ++ .../crypto/AsymmetricCryptoProvider.java | 195 +++ .../sequencing/crypto/CryptoException.java | 34 + .../sequencing/crypto/CryptoProvider.java | 66 + .../crypto/DefaultKeyConverter.java | 77 + .../crypto/EncryptionException.java | 32 + .../crypto/EncryptionMaterials.java | 60 + .../crypto/InvalidKeyException.java | 34 + .../spapi/sequencing/crypto/KeyConverter.java | 50 + .../sequencing/crypto/SignatureException.java | 33 + .../crypto/SignatureGenerationException.java | 33 + .../crypto/SignatureValidationException.java | 34 + .../crypto/SymmetricCryptoProvider.java | 158 +++ .../certs/InternalAndExternalTrustStore.jks | Bin 0 -> 106525 bytes .../spapi/CreateDestinationAndUploadTest.java | 107 ++ .../com/amazon/spapi/ReportDownloadTest.java | 70 + .../Feed_101__POST_PRODUCT_DATA_.xml | 1 + 89 files changed, 8830 insertions(+), 1 deletion(-) create mode 100644 clients/sellingpartner-api-aa-csharp/.gitignore create mode 100644 clients/sellingpartner-api-aa-csharp/README.md create mode 100644 clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/IDateHelper.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMeta.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMetaBuilder.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationCredentials.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationSigner.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/ScopeConstants.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/SigningDateHelper.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Utils.cs create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache create mode 100644 clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api_test.mustache create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAccessTokenRequestMetaBuilderTest.cs create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAuthorizationSignerTest.cs create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs create mode 100644 clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/UtilsTest.cs create mode 100644 clients/sellingpartner-api-aa-java/.gitignore create mode 100644 clients/sellingpartner-api-aa-java/README.md create mode 100644 clients/sellingpartner-api-aa-java/pom.xml create mode 100644 clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache create mode 100644 clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/StringUtil.mustache create mode 100644 clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAccessTokenRequestMeta.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationCredentials.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopes.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializer.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/ScopeConstants.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java create mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java create mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java create mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializerTest.java create mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java create mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java create mode 100644 clients/sellingpartner-api-frp-helper-java/.gitignore create mode 100644 clients/sellingpartner-api-frp-helper-java/README.md create mode 100644 clients/sellingpartner-api-frp-helper-java/build.gradle create mode 100644 clients/sellingpartner-api-frp-helper-java/libs/sellingpartner-api-aa-java-1.0.jar create mode 100644 clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-feeds-java-1.0.jar create mode 100644 clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-reports-java-1.0.jar create mode 100644 clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-uploads-java-1.0.jar create mode 100644 clients/sellingpartner-api-frp-helper-java/pom.xml create mode 100644 clients/sellingpartner-api-frp-helper-java/settings.gradle create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DocumentValidatorInputStream.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DownloadsSequencer.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/SpoolingLimitExceededException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadDetails.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadsSequencer.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/ValidationFailureException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/AccessMechanism.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/DownloadsSequencerImpl.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/HttpClientFactory.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SPAPIUploadDestinationsHttpClientFactory.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SequencerHelper.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/UploadsSequencerImpl.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/DemuxOutputStream.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/TempInputStream.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/AsymmetricCryptoProvider.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoProvider.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/DefaultKeyConverter.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionMaterials.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/InvalidKeyException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/KeyConverter.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureGenerationException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureValidationException.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SymmetricCryptoProvider.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/main/resources/certs/InternalAndExternalTrustStore.jks create mode 100644 clients/sellingpartner-api-frp-helper-java/src/test/java/com/amazon/spapi/CreateDestinationAndUploadTest.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/test/java/com/amazon/spapi/ReportDownloadTest.java create mode 100644 clients/sellingpartner-api-frp-helper-java/src/test/resources/Feed_101__POST_PRODUCT_DATA_.xml diff --git a/README.md b/README.md index ca0b9b1..0c20700 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,3 @@ See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more inform ## License This project is licensed under the Apache-2.0 License. - diff --git a/clients/sellingpartner-api-aa-csharp/.gitignore b/clients/sellingpartner-api-aa-csharp/.gitignore new file mode 100644 index 0000000..d4e324f --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/.gitignore @@ -0,0 +1,10 @@ +#Ignore bin directories +bin +**/bin +#Ignore obj directories +obj +**/obj +#ignore .vs directories +.vs +#misc +.DS_Store diff --git a/clients/sellingpartner-api-aa-csharp/README.md b/clients/sellingpartner-api-aa-csharp/README.md new file mode 100644 index 0000000..a58a97c --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/README.md @@ -0,0 +1,96 @@ +# Selling Partner API Authentication/Authorization Library +This library provides helper classes for use when signing HTTP requests for Amazon Selling Partner APIs. It is intended for use +with the Selling Partner API Client Libraries generated by [swagger codegen](https://swagger.io/tools/swagger-codegen/) +using the RestSharp library. It can also be integrated into custom projects. + +## LWAAuthorizationSigner +Obtains and signs a request with an access token from LWA (Login with Amazon) for the specified endpoint using the provided LWA credentials. + +*Example* +``` +using RestSharp; +using Amazon.SellingPartnerAPIAA; + +string resource = "/my/api/path"; +RestClient restClient = new RestClient("https://..."); +IRestRequest restRequest = new RestRequest(resource, Method.GET); + +// Seller APIs +LWAAuthorizationCredentials lwaAuthorizationCredentials = new LWAAuthorizationCredentials +{ + ClientId = "...", + ClientSecret = "", + RefreshToken = "", + Endpoint = new Uri("...") +}; + +/* Sellerless APIs +The Selling Partner API scopes can be retrieved from the ScopeConstants class and used to specify a list of scopes of an LWAAuthorizationCredentials instance. */ +LWAAuthorizationCredentials lwaAuthorizationCredentials = new LWAAuthorizationCredentials +{ + ClientId = "...", + ClientSecret = "", + Scopes = new List() { ScopeConstants.ScopeNotificationsAPI, ScopeConstants.ScopeMigrationAPI } + Endpoint = new Uri("...") +}; + +restRequest = new LWAAuthorizationSigner(lwaAuthorizationCredentials).Sign(restRequest); +``` +Note the IRestRequest reference is treated as **mutable** when signed. + +## AWSSigV4Signer +Signs a request with [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) +using the provided AWS developer account credentials. + +*Example* +``` +using RestSharp; +using Amazon.SellingPartnerAPIAA; + +string resource = "/my/api/path"; +RestClient restClient = new RestClient("https://..."); +IRestRequest restRequest = new RestRequest(resource, Method.GET); + +AWSAuthenticationCredentials awsAuthenticationCredentials = new AWSAuthenticationCredentials +{ + AccessKeyId = "..." + SecretKey = "..." + Region = "..." +}; + +restRequest = new AWSSigV4Signer(awsAuthenticationCredentials) + .Sign(restRequest, restClient.BaseUrl.Host); +``` +Note the IRestRequest reference is treated as **mutable** when signed. + +## Resources +This package features Mustache templates designed for use with [swagger codegen](https://swagger.io/tools/swagger-codegen/). +When you build Selling Partner API Swagger models with these templates, they help generate a rich SDK with functionality to invoke Selling Partner APIs built in. The templates are located in *resources/swagger-codegen*. + +## Building +This package is built as a .NET Standard Library via a Visual Studio Solution with implementation and test projects. The Visual Studio Community Edition can be obtained for free from Microsoft and used to build the solution and generate a .dll assembly which can be imported into other projects. + +## Dependencies +All dependencies can be installed via NuGet +- RestSharp - 105.1.0 +- Newtonsoft.Json 12.0.3 +- NETStandard.Library 2.0.3 (platform-specific implementation requirements are documented on the [Microsoft .NET Guide](https://docs.microsoft.com/en-us/dotnet/standard/net-standard)) + +## License +Swagger Codegen templates are subject to the [Swagger Codegen License](https://github.com/swagger-api/swagger-codegen#license). + +All other work licensed as follows: + +Copyright 2020 Amazon.com, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this library except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln b/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln new file mode 100644 index 0000000..bdbc902 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/SellingPartnerAPIAuthAndAuthCSharp.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.SellingPartnerAPIAA", "src\Amazon.SellingPartnerAPIAA\Amazon.SellingPartnerAPIAA.csproj", "{64339397-3AAB-49D3-8B50-7A467B16D545}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.SellingPartnerAPIAATests", "test\Amazon.SellingPartnerAPIAATests\Amazon.SellingPartnerAPIAATests.csproj", "{12B130EB-1087-4F88-BDFA-3088868C0A46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {64339397-3AAB-49D3-8B50-7A467B16D545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64339397-3AAB-49D3-8B50-7A467B16D545}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64339397-3AAB-49D3-8B50-7A467B16D545}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64339397-3AAB-49D3-8B50-7A467B16D545}.Release|Any CPU.Build.0 = Release|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12B130EB-1087-4F88-BDFA-3088868C0A46}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs new file mode 100644 index 0000000..b415738 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSAuthenticationCredentials.cs @@ -0,0 +1,20 @@ +namespace Amazon.SellingPartnerAPIAA +{ + public class AWSAuthenticationCredentials + { + /** + * AWS IAM User Access Key Id + */ + public string AccessKeyId { get; set; } + + /** + * AWS IAM User Secret Key + */ + public string SecretKey { get; set; } + + /** + * AWS Region + */ + public string Region { get; set; } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs new file mode 100644 index 0000000..9f7e3db --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSigV4Signer.cs @@ -0,0 +1,82 @@ +using System; +using System.Text; +using RestSharp; + +namespace Amazon.SellingPartnerAPIAA +{ + public class AWSSigV4Signer + + { + public virtual AWSSignerHelper AwsSignerHelper { get; set; } + private AWSAuthenticationCredentials awsCredentials; + + /// + /// Constructor for AWSSigV4Signer + /// + /// AWS Developer Account Credentials + public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials) + { + awsCredentials = awsAuthenticationCredentials; + AwsSignerHelper = new AWSSignerHelper(); + } + + /// + /// Signs a Request with AWS Signature Version 4 + /// + /// RestRequest which needs to be signed + /// Request endpoint + /// RestRequest with AWS Signature + public IRestRequest Sign(IRestRequest request, string host) + { + DateTime signingDate = AwsSignerHelper.InitializeHeaders(request, host); + string signedHeaders = AwsSignerHelper.ExtractSignedHeaders(request); + + string hashedCanonicalRequest = CreateCanonicalRequest(request, signedHeaders); + + string stringToSign = AwsSignerHelper.BuildStringToSign(signingDate, + hashedCanonicalRequest, + awsCredentials.Region); + + string signature = AwsSignerHelper.CalculateSignature(stringToSign, + signingDate, + awsCredentials.SecretKey, + awsCredentials.Region); + + AwsSignerHelper.AddSignature(request, + awsCredentials.AccessKeyId, + signedHeaders, + signature, + awsCredentials.Region, + signingDate); + + return request; + } + + private string CreateCanonicalRequest(IRestRequest restRequest, string signedHeaders) + { + var canonicalizedRequest = new StringBuilder(); + //Request Method + canonicalizedRequest.AppendFormat("{0}\n", restRequest.Method); + + //CanonicalURI + canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalURIParameters(restRequest.Resource)); + + //CanonicalQueryString + canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalQueryString(restRequest)); + + //CanonicalHeaders + canonicalizedRequest.AppendFormat("{0}\n", AwsSignerHelper.ExtractCanonicalHeaders(restRequest)); + + //SignedHeaders + canonicalizedRequest.AppendFormat("{0}\n", signedHeaders); + + // Hash(digest) the payload in the body + canonicalizedRequest.AppendFormat(AwsSignerHelper.HashRequestBody(restRequest)); + + string canonicalRequest = canonicalizedRequest.ToString(); + + //Create a digest(hash) of the canonical request + return Utils.ToHex(Utils.Hash(canonicalRequest)); + } + } +} \ No newline at end of file diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs new file mode 100644 index 0000000..5a2f7ef --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/AWSSignerHelper.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RestSharp; +using System.Text.RegularExpressions; +using System.Globalization; + +namespace Amazon.SellingPartnerAPIAA +{ + public class AWSSignerHelper + { + public const string ISO8601BasicDateTimeFormat = "yyyyMMddTHHmmssZ"; + public const string ISO8601BasicDateFormat = "yyyyMMdd"; + + public const string XAmzDateHeaderName = "X-Amz-Date"; + public const string AuthorizationHeaderName = "Authorization"; + public const string CredentialSubHeaderName = "Credential"; + public const string SignatureSubHeaderName = "Signature"; + public const string SignedHeadersSubHeaderName = "SignedHeaders"; + public const string HostHeaderName = "host"; + + public const string Scheme = "AWS4"; + public const string Algorithm = "HMAC-SHA256"; + public const string TerminationString = "aws4_request"; + public const string ServiceName = "execute-api"; + public const string Slash = "/"; + + private readonly static Regex CompressWhitespaceRegex = new Regex("\\s+"); + + public virtual IDateHelper DateHelper { get; set; } + + public AWSSignerHelper() + { + DateHelper = new SigningDateHelper(); + } + + /// + /// Returns URI encoded version of absolute path + /// + /// Resource path(absolute path) from the request + /// URI encoded version of absolute path + public virtual string ExtractCanonicalURIParameters(string resource) + { + string canonicalUri = string.Empty; + + if (string.IsNullOrEmpty(resource)) + { + canonicalUri = Slash; + } + else + { + if (!resource.StartsWith(Slash)) + { + canonicalUri = Slash; + } + //Split path at / into segments + IEnumerable encodedSegments = resource.Split(new char[] { '/' }, StringSplitOptions.None); + + // Encode twice + encodedSegments = encodedSegments.Select(segment => Utils.UrlEncode(segment)); + encodedSegments = encodedSegments.Select(segment => Utils.UrlEncode(segment)); + + canonicalUri += string.Join(Slash, encodedSegments.ToArray()); + } + + return canonicalUri; + } + + /// + /// Returns query parameters in canonical order with URL encoding + /// + /// RestRequest + /// Query parameters in canonical order with URL encoding + public virtual string ExtractCanonicalQueryString(IRestRequest request) + { + IDictionary queryParameters = request.Parameters + .Where(parameter => ParameterType.QueryString.Equals(parameter.Type)) + .ToDictionary(header => header.Name.Trim().ToString(), header => header.Value.ToString()); + + SortedDictionary sortedqueryParameters = new SortedDictionary(queryParameters); + + StringBuilder canonicalQueryString = new StringBuilder(); + foreach (var key in sortedqueryParameters.Keys) + { + if (canonicalQueryString.Length > 0) + { + canonicalQueryString.Append("&"); + } + canonicalQueryString.AppendFormat("{0}={1}", + Utils.UrlEncode(key), + Utils.UrlEncode(sortedqueryParameters[key])); + } + + return canonicalQueryString.ToString(); + } + + /// + /// Returns Http headers in canonical order with all header names to lowercase + /// + /// RestRequest + /// Returns Http headers in canonical order + public virtual string ExtractCanonicalHeaders(IRestRequest request) + { + IDictionary headers = request.Parameters + .Where(parameter => ParameterType.HttpHeader.Equals(parameter.Type)) + .ToDictionary(header => header.Name.Trim().ToLowerInvariant(), header => header.Value.ToString()); + + SortedDictionary sortedHeaders = new SortedDictionary(headers); + + StringBuilder headerString = new StringBuilder(); + + foreach (string headerName in sortedHeaders.Keys) + { + headerString.AppendFormat("{0}:{1}\n", + headerName, + CompressWhitespaceRegex.Replace(sortedHeaders[headerName].Trim(), " ")); + } + + return headerString.ToString(); + } + + /// + /// Returns list(as string) of Http headers in canonical order + /// + /// RestRequest + /// List of Http headers in canonical order + public virtual string ExtractSignedHeaders(IRestRequest request) + { + List rawHeaders = request.Parameters.Where(parameter => ParameterType.HttpHeader.Equals(parameter.Type)) + .Select(header => header.Name.Trim().ToLowerInvariant()) + .ToList(); + rawHeaders.Sort(StringComparer.OrdinalIgnoreCase); + + return string.Join(";", rawHeaders); + } + + /// + /// Returns hexadecimal hashed value(using SHA256) of payload in the body of request + /// + /// RestRequest + /// Hexadecimal hashed value of payload in the body of request + public virtual string HashRequestBody(IRestRequest request) + { + Parameter body = request.Parameters.FirstOrDefault(parameter => ParameterType.RequestBody.Equals(parameter.Type)); + string value = body != null ? body.Value.ToString() : string.Empty; + return Utils.ToHex(Utils.Hash(value)); + } + + /// + /// Builds the string for signing using signing date, hashed canonical request and region + /// + /// Signing Date + /// Hashed Canonical Request + /// Region + /// String to be used for signing + public virtual string BuildStringToSign(DateTime signingDate, string hashedCanonicalRequest, string region) + { + string scope = BuildScope(signingDate, region); + string stringToSign = string.Format(CultureInfo.InvariantCulture, "{0}-{1}\n{2}\n{3}\n{4}", + Scheme, + Algorithm, + signingDate.ToString(ISO8601BasicDateTimeFormat, CultureInfo.InvariantCulture), + scope, + hashedCanonicalRequest); + + return stringToSign; + } + + /// + /// Sets AWS4 mandated 'x-amz-date' header, returning the date/time that will + /// be used throughout the signing process. + /// + /// RestRequest + /// Request endpoint + /// Date and time used for x-amz-date, in UTC + public virtual DateTime InitializeHeaders(IRestRequest restRequest, string host) + { + restRequest.Parameters.RemoveAll(parameter => ParameterType.HttpHeader.Equals(parameter.Type) + && parameter.Name == XAmzDateHeaderName); + restRequest.Parameters.RemoveAll(parameter => ParameterType.HttpHeader.Equals(parameter.Type) + && parameter.Name == HostHeaderName); + + DateTime signingDate = DateHelper.GetUtcNow(); + + restRequest.AddHeader(XAmzDateHeaderName, signingDate.ToString(ISO8601BasicDateTimeFormat, CultureInfo.InvariantCulture)); + restRequest.AddHeader(HostHeaderName, host); + + return signingDate; + } + + /// + /// Calculates AWS4 signature for the string, prepared for signing + /// + /// String to be signed + /// Signing Date + /// Secret Key + /// Region + /// AWS4 Signature + public virtual string CalculateSignature(string stringToSign, + DateTime signingDate, + string secretKey, + string region) + { + string date = signingDate.ToString(ISO8601BasicDateFormat, CultureInfo.InvariantCulture); + byte[] kSecret = Encoding.UTF8.GetBytes(Scheme + secretKey); + byte[] kDate = Utils.GetKeyedHash(kSecret, date); + byte[] kRegion = Utils.GetKeyedHash(kDate, region); + byte[] kService = Utils.GetKeyedHash(kRegion, ServiceName); + byte[] kSigning = Utils.GetKeyedHash(kService, TerminationString); + + // Calculate the signature + return Utils.ToHex(Utils.GetKeyedHash(kSigning, stringToSign)); + } + + /// + /// Add a signature to a request in the form of an 'Authorization' header + /// + /// Request to be signed + /// Access Key Id + /// Signed Headers + /// The signature to add + /// AWS region for the request + /// Signature date + public virtual void AddSignature(IRestRequest restRequest, + string accessKeyId, + string signedHeaders, + string signature, + string region, + DateTime signingDate) + { + string scope = BuildScope(signingDate, region); + StringBuilder authorizationHeaderValueBuilder = new StringBuilder(); + authorizationHeaderValueBuilder.AppendFormat("{0}-{1}", Scheme, Algorithm); + authorizationHeaderValueBuilder.AppendFormat(" {0}={1}/{2},", CredentialSubHeaderName, accessKeyId, scope); + authorizationHeaderValueBuilder.AppendFormat(" {0}={1},", SignedHeadersSubHeaderName, signedHeaders); + authorizationHeaderValueBuilder.AppendFormat(" {0}={1}", SignatureSubHeaderName, signature); + + restRequest.AddHeader(AuthorizationHeaderName, authorizationHeaderValueBuilder.ToString()); + } + + private static string BuildScope(DateTime signingDate, string region) + { + return string.Format("{0}/{1}/{2}/{3}", + signingDate.ToString(ISO8601BasicDateFormat, CultureInfo.InvariantCulture), + region, + ServiceName, + TerminationString); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj new file mode 100644 index 0000000..b357409 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Amazon.SellingPartnerAPIAA.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/IDateHelper.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/IDateHelper.cs new file mode 100644 index 0000000..175076c --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/IDateHelper.cs @@ -0,0 +1,8 @@ +using System; +namespace Amazon.SellingPartnerAPIAA +{ + public interface IDateHelper + { + DateTime GetUtcNow(); + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMeta.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMeta.cs new file mode 100644 index 0000000..1188272 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMeta.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Amazon.SellingPartnerAPIAA +{ + public class LWAAccessTokenRequestMeta + { + [JsonProperty(PropertyName = "grant_type")] + public string GrantType { get; set; } + + [JsonProperty(PropertyName = "refresh_token")] + public string RefreshToken { get; set; } + + [JsonProperty(PropertyName = "client_id")] + public string ClientId { get; set; } + + [JsonProperty(PropertyName = "client_secret")] + public string ClientSecret { get; set; } + + [JsonProperty(PropertyName = "scope")] + public string Scope { get; set; } + + public override bool Equals(object obj) + { + LWAAccessTokenRequestMeta other = obj as LWAAccessTokenRequestMeta; + + return other != null && + this.GrantType == other.GrantType && + this.RefreshToken == other.RefreshToken && + this.ClientId == other.ClientId && + this.ClientSecret == other.ClientSecret && + this.Scope == other.Scope; + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMetaBuilder.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMetaBuilder.cs new file mode 100644 index 0000000..d6e8bcf --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAccessTokenRequestMetaBuilder.cs @@ -0,0 +1,40 @@ +using System.Linq; + +namespace Amazon.SellingPartnerAPIAA +{ + public class LWAAccessTokenRequestMetaBuilder + { + public const string SellerAPIGrantType = "refresh_token"; + public const string SellerlessAPIGrantType = "client_credentials"; + + private const string Delimiter = " "; + + /// + /// Builds an instance of LWAAccessTokenRequestMeta modeling appropriate LWA token + /// request params based on configured LWAAuthorizationCredentials + /// + /// LWA Authorization Credentials + /// + public virtual LWAAccessTokenRequestMeta Build(LWAAuthorizationCredentials lwaAuthorizationCredentials) + { + LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta = new LWAAccessTokenRequestMeta() + { + ClientId = lwaAuthorizationCredentials.ClientId, + ClientSecret = lwaAuthorizationCredentials.ClientSecret, + RefreshToken = lwaAuthorizationCredentials.RefreshToken + }; + + if (lwaAuthorizationCredentials.Scopes == null || lwaAuthorizationCredentials.Scopes.Count == 0) + { + lwaAccessTokenRequestMeta.GrantType = SellerAPIGrantType; + } + else + { + lwaAccessTokenRequestMeta.Scope = string.Join(Delimiter, lwaAuthorizationCredentials.Scopes); + lwaAccessTokenRequestMeta.GrantType = SellerlessAPIGrantType; + } + + return lwaAccessTokenRequestMeta; + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationCredentials.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationCredentials.cs new file mode 100644 index 0000000..414dcad --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationCredentials.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace Amazon.SellingPartnerAPIAA +{ + public class LWAAuthorizationCredentials + { + public LWAAuthorizationCredentials() + { + this.Scopes = new List(); + } + + /** + * LWA Client Id + */ + public string ClientId { get; set; } + + /** + * LWA Client Secret + */ + public string ClientSecret { get; set; } + + /** + * LWA Refresh Token + */ + public string RefreshToken { get; set; } + + /** + * LWA Authorization Server Endpoint + */ + public Uri Endpoint { get; set; } + + /** + * LWA Authorization Scopes + */ + public List Scopes { get; set; } + } +} + diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationSigner.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationSigner.cs new file mode 100644 index 0000000..bc64308 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAAuthorizationSigner.cs @@ -0,0 +1,34 @@ +using RestSharp; + +namespace Amazon.SellingPartnerAPIAA +{ + public class LWAAuthorizationSigner + { + public const string AccessTokenHeaderName = "x-amz-access-token"; + + public LWAClient LWAClient { get; set; } + + /// + /// Constructor for LWAAuthorizationSigner + /// + /// LWA Authorization Credentials for token exchange + public LWAAuthorizationSigner(LWAAuthorizationCredentials lwaAuthorizationCredentials) + { + LWAClient = new LWAClient(lwaAuthorizationCredentials); + } + + /// + /// Signs a request with LWA Access Token + /// + /// Request to sign + /// restRequest with LWA signature + public IRestRequest Sign(IRestRequest restRequest) + { + string accessToken = LWAClient.GetAccessToken(); + + restRequest.AddHeader(AccessTokenHeaderName, accessToken); + + return restRequest; + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs new file mode 100644 index 0000000..dfcd4d4 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/LWAClient.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RestSharp; + +namespace Amazon.SellingPartnerAPIAA +{ + public class LWAClient + { + public const string AccessTokenKey = "access_token"; + public const string JsonMediaType = "application/json; charset=utf-8"; + + public IRestClient RestClient { get; set; } + public LWAAccessTokenRequestMetaBuilder LWAAccessTokenRequestMetaBuilder { get; set; } + public LWAAuthorizationCredentials LWAAuthorizationCredentials { get; private set; } + + + public LWAClient(LWAAuthorizationCredentials lwaAuthorizationCredentials) + { + LWAAuthorizationCredentials = lwaAuthorizationCredentials; + LWAAccessTokenRequestMetaBuilder = new LWAAccessTokenRequestMetaBuilder(); + RestClient = new RestClient(LWAAuthorizationCredentials.Endpoint.GetLeftPart(System.UriPartial.Authority)); + } + + /// + /// Retrieves access token from LWA + /// + /// LWA AccessTokenRequest metadata + /// LWA Access Token + public virtual string GetAccessToken() + { + LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta = LWAAccessTokenRequestMetaBuilder.Build(LWAAuthorizationCredentials); + var accessTokenRequest = new RestRequest(LWAAuthorizationCredentials.Endpoint.AbsolutePath, Method.POST); + + string jsonRequestBody = JsonConvert.SerializeObject(lwaAccessTokenRequestMeta); + + accessTokenRequest.AddParameter(JsonMediaType, jsonRequestBody, ParameterType.RequestBody); + + string accessToken; + try + { + var response = RestClient.Execute(accessTokenRequest); + + if (!IsSuccessful(response)) + { + throw new IOException("Unsuccessful LWA token exchange", response.ErrorException); + } + + JObject responseJson = JObject.Parse(response.Content); + + accessToken = responseJson.GetValue(AccessTokenKey).ToString(); + } + catch (Exception e) + { + throw new SystemException("Error getting LWA Access Token", e); + } + + return accessToken; + } + + private bool IsSuccessful(IRestResponse response) + { + int statusCode = (int)response.StatusCode; + return statusCode >= 200 && statusCode <= 299 && response.ResponseStatus == ResponseStatus.Completed; + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/ScopeConstants.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/ScopeConstants.cs new file mode 100644 index 0000000..c9cf798 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/ScopeConstants.cs @@ -0,0 +1,8 @@ +namespace Amazon.SellingPartnerAPIAA +{ + public class ScopeConstants + { + public const string ScopeNotificationsAPI = "sellingpartnerapi::notifications"; + public const string ScopeMigrationAPI = "sellingpartnerapi::migration"; + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/SigningDateHelper.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/SigningDateHelper.cs new file mode 100644 index 0000000..cc77d75 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/SigningDateHelper.cs @@ -0,0 +1,11 @@ +using System; +namespace Amazon.SellingPartnerAPIAA +{ + public class SigningDateHelper : IDateHelper + { + public DateTime GetUtcNow() + { + return DateTime.UtcNow; + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Utils.cs b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Utils.cs new file mode 100644 index 0000000..a96f9ad --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/Utils.cs @@ -0,0 +1,73 @@ +using System.Text; +using System.Security.Cryptography; +using System.Globalization; + +namespace Amazon.SellingPartnerAPIAA +{ + public static class Utils + { + public const string ValidUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; + + /// + /// Returns URL encoded version of input data according to RFC-3986 + /// + /// String to be URL-encoded + /// URL encoded version of input data + public static string UrlEncode(string data) + { + StringBuilder encoded = new StringBuilder(); + foreach (char symbol in Encoding.UTF8.GetBytes(data)) + { + if (ValidUrlCharacters.IndexOf(symbol) != -1) + { + encoded.Append(symbol); + } + else + { + encoded.Append("%").Append(string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)symbol)); + } + } + return encoded.ToString(); + } + + /// + /// Returns hashed value of input data using SHA256 + /// + /// String to be hashed + /// Hashed value of input data + public static byte[] Hash(string data) + { + return new SHA256CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(data)); + } + + /// + /// Returns lowercase hexadecimal string of input byte array + /// + /// Data to be converted + /// Lowercase hexadecimal string + public static string ToHex(byte[] data) + { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < data.Length; i++) + { + sb.Append(data[i].ToString("x2", CultureInfo.InvariantCulture)); + } + + return sb.ToString(); + } + + /// + /// Computes the hash of given string using the specified key with HMACSHA256 + /// + /// Key + /// String to be hashed + /// Hashed value of input data + public static byte[] GetKeyedHash(byte[] key, string value) + { + KeyedHashAlgorithm hashAlgorithm = new HMACSHA256(key); + hashAlgorithm.Initialize(); + return hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(value)); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache new file mode 100644 index 0000000..3a98a34 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/ApiClient.mustache @@ -0,0 +1,576 @@ +{{>partial_header}} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text.RegularExpressions; +using System.IO; +{{^netStandard}} +{{^supportsUWP}} +using System.Web; +{{/supportsUWP}} +{{/netStandard}} +using System.Linq; +using System.Net; +using System.Text; +using Newtonsoft.Json; +{{#netStandard}} +using RestSharp.Portable; +using RestSharp.Portable.HttpClient; +{{/netStandard}} +{{^netStandard}} +using RestSharp; +{{/netStandard}} +using Amazon.SellingPartnerAPIAA; + +namespace {{packageName}}.Client +{ + /// + /// API client is mainly responsible for making the HTTP call to the API backend. + /// + {{>visibility}} partial class ApiClient + { + private LWAAuthorizationSigner lwaAuthorizationSigner; + private AWSSigV4Signer awsSigV4Signer; + + private JsonSerializerSettings serializerSettings = new JsonSerializerSettings + { + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor + }; + + /// + /// Allows for extending request processing for generated code. + /// + /// The RestSharp request object + private void InterceptRequest(IRestRequest request) + { + lwaAuthorizationSigner.Sign(request); + awsSigV4Signer.Sign(request, RestClient.BaseUrl.Host); + } + + /// + /// Allows for extending response processing for generated code. + /// + /// The RestSharp request object + /// The RestSharp response object + partial void InterceptResponse(IRestRequest request, IRestResponse response); + + /// + /// Initializes a new instance of the class + /// with default base path ({{{basePath}}}). + /// + /// An instance of Configuration. + public ApiClient(Configuration config) + { + Configuration = config ?? {{packageName}}.Client.Configuration.Default; + + RestClient = new RestClient(Configuration.BasePath); + {{#netStandard}} + RestClient.IgnoreResponseStatusCode = true; + {{/netStandard}} + + lwaAuthorizationSigner = new LWAAuthorizationSigner(Configuration.AuthorizationCredentials); + awsSigV4Signer = new AWSSigV4Signer(Configuration.AuthenticationCredentials); + } + + /// + /// Gets or sets the default API client for making HTTP calls. + /// + /// The default API client. + [Obsolete("ApiClient.Default is deprecated, please use 'Configuration.Default.ApiClient' instead.")] + public static ApiClient Default; + + /// + /// Gets or sets an instance of the IReadableConfiguration. + /// + /// An instance of the IReadableConfiguration. + /// + /// helps us to avoid modifying possibly global + /// configuration values from within a given client. It does not guarantee thread-safety + /// of the instance in any way. + /// + public IReadableConfiguration Configuration { get; set; } + + /// + /// Gets or sets the RestClient. + /// + /// An instance of the RestClient + public RestClient RestClient { get; set; } + + // Creates and sets up a RestRequest prior to a call. + private RestRequest PrepareRequest( + String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, List> queryParams, Object postBody, + Dictionary headerParams, Dictionary formParams, + Dictionary fileParams, Dictionary pathParams, + String contentType) + { + var request = new RestRequest(path, method); + {{#netStandard}} + // disable ResetSharp.Portable built-in serialization + request.Serializer = null; + {{/netStandard}} + + // add path parameter, if any + foreach(var param in pathParams) + request.AddParameter(param.Key, param.Value, ParameterType.UrlSegment); + + // add header parameter, if any + foreach(var param in headerParams) + request.AddHeader(param.Key, param.Value); + + // add query parameter, if any + foreach(var param in queryParams) + request.AddQueryParameter(param.Key, param.Value); + + // add form parameter, if any + foreach(var param in formParams) + request.AddParameter(param.Key, param.Value); + + // add file parameter, if any + foreach(var param in fileParams) + { + {{#netStandard}} + request.AddFile(param.Value); + {{/netStandard}} + {{^netStandard}} + {{^supportsUWP}} + request.AddFile(param.Value.Name, param.Value.Writer, param.Value.FileName, param.Value.ContentType); + {{/supportsUWP}} + {{#supportsUWP}} + byte[] paramWriter = null; + param.Value.Writer = delegate (Stream stream) { paramWriter = ToByteArray(stream); }; + request.AddFile(param.Value.Name, paramWriter, param.Value.FileName, param.Value.ContentType); + {{/supportsUWP}} + {{/netStandard}} + } + + if (postBody != null) // http body (model or byte[]) parameter + { + {{#netStandard}} + request.AddParameter(new Parameter { Value = postBody, Type = ParameterType.RequestBody, ContentType = contentType }); + {{/netStandard}} + {{^netStandard}} + request.AddParameter(contentType, postBody, ParameterType.RequestBody); + {{/netStandard}} + } + + return request; + } + + /// + /// Makes the HTTP request (Sync). + /// + /// URL path. + /// HTTP method. + /// Query parameters. + /// HTTP body (POST request). + /// Header parameters. + /// Form parameters. + /// File parameters. + /// Path parameters. + /// Content Type of the request + /// Object + public Object CallApi( + String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, List> queryParams, Object postBody, + Dictionary headerParams, Dictionary formParams, + Dictionary fileParams, Dictionary pathParams, + String contentType) + { + var request = PrepareRequest( + path, method, queryParams, postBody, headerParams, formParams, fileParams, + pathParams, contentType); + + // set timeout + {{#netStandard}}RestClient.Timeout = TimeSpan.FromMilliseconds(Configuration.Timeout);{{/netStandard}} + {{^netStandard}}RestClient.Timeout = Configuration.Timeout;{{/netStandard}} + // set user agent + RestClient.UserAgent = Configuration.UserAgent; + + InterceptRequest(request); + {{#netStandard}} + var response = RestClient.Execute(request).Result; + {{/netStandard}} + {{^netStandard}} + {{^supportsUWP}} + var response = RestClient.Execute(request); + {{/supportsUWP}} + {{#supportsUWP}} + // Using async method to perform sync call (uwp-only) + var response = RestClient.ExecuteTaskAsync(request).Result; + {{/supportsUWP}} + {{/netStandard}} + InterceptResponse(request, response); + + return (Object) response; + } + {{#supportsAsync}} + /// + /// Makes the asynchronous HTTP request. + /// + /// URL path. + /// HTTP method. + /// Query parameters. + /// HTTP body (POST request). + /// Header parameters. + /// Form parameters. + /// File parameters. + /// Path parameters. + /// Content type. + /// The Task instance. + public async System.Threading.Tasks.Task CallApiAsync( + String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, List> queryParams, Object postBody, + Dictionary headerParams, Dictionary formParams, + Dictionary fileParams, Dictionary pathParams, + String contentType) + { + var request = PrepareRequest( + path, method, queryParams, postBody, headerParams, formParams, fileParams, + pathParams, contentType); + InterceptRequest(request); + var response = await RestClient.Execute{{^netStandard}}TaskAsync{{/netStandard}}(request); + InterceptResponse(request, response); + return (Object)response; + }{{/supportsAsync}} + + /// + /// Escape string (url-encoded). + /// + /// String to be escaped. + /// Escaped string. + public string EscapeString(string str) + { + return UrlEncode(str); + } + + /// + /// Create FileParameter based on Stream. + /// + /// Parameter name. + /// Input stream. + /// FileParameter. + public FileParameter ParameterToFile(string name, Stream stream) + { + if (stream is FileStream) + return FileParameter.Create(name, ReadAsBytes(stream), Path.GetFileName(((FileStream)stream).Name)); + else + return FileParameter.Create(name, ReadAsBytes(stream), "no_file_name_provided"); + } + + /// + /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime. + /// If parameter is a list, join the list with ",". + /// Otherwise just return the string. + /// + /// The parameter (header, path, query, form). + /// Formatted string. + public string ParameterToString(object obj) + { + if (obj is DateTime) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return ((DateTime)obj).ToString (Configuration.DateTimeFormat); + else if (obj is DateTimeOffset) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return ((DateTimeOffset)obj).ToString (Configuration.DateTimeFormat); + else if (obj is IList) + { + var flattenedString = new StringBuilder(); + foreach (var param in (IList)obj) + { + if (flattenedString.Length > 0) + flattenedString.Append(","); + flattenedString.Append(param); + } + return flattenedString.ToString(); + } + else + return Convert.ToString (obj); + } + + /// + /// Deserialize the JSON string into a proper object. + /// + /// The HTTP response. + /// Object type. + /// Object representation of the JSON string. + public object Deserialize(IRestResponse response, Type type) + { + {{^netStandard}}IList{{/netStandard}}{{#netStandard}}IHttpHeaders{{/netStandard}} headers = response.Headers; + if (type == typeof(byte[])) // return byte array + { + return response.RawBytes; + } + + // TODO: ? if (type.IsAssignableFrom(typeof(Stream))) + if (type == typeof(Stream)) + { + if (headers != null) + { + var filePath = String.IsNullOrEmpty(Configuration.TempFolderPath) + ? Path.GetTempPath() + : Configuration.TempFolderPath; + var regex = new Regex(@"Content-Disposition=.*filename=['""]?([^'""\s]+)['""]?$"); + foreach (var header in headers) + { + var match = regex.Match(header.ToString()); + if (match.Success) + { + string fileName = filePath + SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); + File.WriteAllBytes(fileName, response.RawBytes); + return new FileStream(fileName, FileMode.Open); + } + } + } + var stream = new MemoryStream(response.RawBytes); + return stream; + } + + if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object + { + return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); + } + + if (type == typeof(String) || type.Name.StartsWith("System.Nullable")) // return primitive type + { + return ConvertType(response.Content, type); + } + + // at this point, it must be a model (json) + try + { + return JsonConvert.DeserializeObject(response.Content, type, serializerSettings); + } + catch (Exception e) + { + throw new ApiException(500, e.Message); + } + } + + /// + /// Serialize an input (model) into JSON string + /// + /// Object. + /// JSON string. + public String Serialize(object obj) + { + try + { + return obj != null ? JsonConvert.SerializeObject(obj) : null; + } + catch (Exception e) + { + throw new ApiException(500, e.Message); + } + } + + /// + ///Check if the given MIME is a JSON MIME. + ///JSON MIME examples: + /// application/json + /// application/json; charset=UTF8 + /// APPLICATION/JSON + /// application/vnd.company+json + /// + /// MIME + /// Returns True if MIME type is json. + public bool IsJsonMime(String mime) + { + var jsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"); + return mime != null && (jsonRegex.IsMatch(mime) || mime.Equals("application/json-patch+json")); + } + + /// + /// Select the Content-Type header's value from the given content-type array: + /// if JSON type exists in the given array, use it; + /// otherwise use the first one defined in 'consumes' + /// + /// The Content-Type array to select from. + /// The Content-Type header to use. + public String SelectHeaderContentType(String[] contentTypes) + { + if (contentTypes.Length == 0) + return "application/json"; + + foreach (var contentType in contentTypes) + { + if (IsJsonMime(contentType.ToLower())) + return contentType; + } + + return contentTypes[0]; // use the first content type specified in 'consumes' + } + + /// + /// Select the Accept header's value from the given accepts array: + /// if JSON exists in the given array, use it; + /// otherwise use all of them (joining into a string) + /// + /// The accepts array to select from. + /// The Accept header to use. + public String SelectHeaderAccept(String[] accepts) + { + if (accepts.Length == 0) + return null; + + if (accepts.Contains("application/json", StringComparer.OrdinalIgnoreCase)) + return "application/json"; + + return String.Join(",", accepts); + } + + /// + /// Encode string in base64 format. + /// + /// String to be encoded. + /// Encoded string. + public static string Base64Encode(string text) + { + return System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text)); + } + + /// + /// Dynamically cast the object into target type. + /// + /// Object to be casted + /// Target type + /// Casted object + {{#supportsAsync}} + public static dynamic ConvertType(dynamic fromObject, Type toObject) + {{/supportsAsync}} + {{^supportsAsync}} + public static object ConvertType(T fromObject, Type toObject) where T : class + {{/supportsAsync}} + { + return Convert.ChangeType(fromObject, toObject); + } + + /// + /// Convert stream to byte array + /// + /// Input stream to be converted + /// Byte array + public static byte[] ReadAsBytes(Stream inputStream) + { + byte[] buf = new byte[16*1024]; + using (MemoryStream ms = new MemoryStream()) + { + int count; + while ((count = inputStream.Read(buf, 0, buf.Length)) > 0) + { + ms.Write(buf, 0, count); + } + return ms.ToArray(); + } + } + + /// + /// URL encode a string + /// Credit/Ref: https://github.com/restsharp/RestSharp/blob/master/RestSharp/Extensions/StringExtensions.cs#L50 + /// + /// String to be URL encoded + /// Byte array + public static string UrlEncode(string input) + { + const int maxLength = 32766; + + if (input == null) + { + throw new ArgumentNullException("input"); + } + + if (input.Length <= maxLength) + { + return Uri.EscapeDataString(input); + } + + StringBuilder sb = new StringBuilder(input.Length * 2); + int index = 0; + + while (index < input.Length) + { + int length = Math.Min(input.Length - index, maxLength); + string subString = input.Substring(index, length); + + sb.Append(Uri.EscapeDataString(subString)); + index += subString.Length; + } + + return sb.ToString(); + } + + /// + /// Sanitize filename by removing the path + /// + /// Filename + /// Filename + public static string SanitizeFilename(string filename) + { + Match match = Regex.Match(filename, @".*[/\\](.*)$"); + + if (match.Success) + { + return match.Groups[1].Value; + } + else + { + return filename; + } + } + {{^netStandard}} + {{#supportsUWP}} + /// + /// Convert stream to byte array + /// + /// IO stream + /// Byte array + public static byte[] ToByteArray(Stream stream) + { + stream.Position = 0; + byte[] buffer = new byte[stream.Length]; + for (int totalBytesCopied = 0; totalBytesCopied < stream.Length;) + totalBytesCopied += stream.Read(buffer, totalBytesCopied, Convert.ToInt32(stream.Length) - totalBytesCopied); + return buffer; + } + {{/supportsUWP}} + {{/netStandard}} + + /// + /// Convert params to key/value pairs. + /// Use collectionFormat to properly format lists and collections. + /// + /// Key name. + /// Value object. + /// A list of KeyValuePairs + public IEnumerable> ParameterToKeyValuePairs(string collectionFormat, string name, object value) + { + var parameters = new List>(); + + if (IsCollection(value) && collectionFormat == "multi") + { + var valueCollection = value as IEnumerable; + parameters.AddRange(from object item in valueCollection select new KeyValuePair(name, ParameterToString(item))); + } + else + { + parameters.Add(new KeyValuePair(name, ParameterToString(value))); + } + + return parameters; + } + + /// + /// Check if generic object is a collection. + /// + /// + /// True if object is a collection type + private static bool IsCollection(object value) + { + return value is IList || value is ICollection; + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache new file mode 100644 index 0000000..77b0154 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/Configuration.mustache @@ -0,0 +1,464 @@ +{{>partial_header}} +using System; +using System.Reflection; +{{^net35}} +using System.Collections.Concurrent; +{{/net35}} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Amazon.SellingPartnerAPIAA; + +namespace {{packageName}}.Client +{ + /// + /// Represents a set of configuration settings + /// + {{>visibility}} class Configuration : IReadableConfiguration + { + #region Constants + + /// + /// Version of the package. + /// + /// Version of the package. + public const string Version = "{{packageVersion}}"; + + /// + /// Identifier for ISO 8601 DateTime Format + /// + /// See https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 for more information. + // ReSharper disable once InconsistentNaming + public const string ISO8601_DATETIME_FORMAT = "o"; + + #endregion Constants + + #region Static Members + + private static readonly object GlobalConfigSync = new { }; + private static Configuration _globalConfiguration; + + /// + /// Default creation of exceptions for a given method name and response object + /// + public static readonly ExceptionFactory DefaultExceptionFactory = (methodName, response) => + { + var status = (int)response.StatusCode; + if (status >= 400) + { + return new ApiException(status, + string.Format("Error calling {0}: {1}", methodName, response.Content), + response.Content); + } + {{^netStandard}}if (status == 0) + { + return new ApiException(status, + string.Format("Error calling {0}: {1}", methodName, response.ErrorMessage), response.ErrorMessage); + }{{/netStandard}} + return null; + }; + + /// + /// Gets or sets the default Configuration. + /// + /// Configuration. + public static Configuration Default + { + get { return _globalConfiguration; } + set + { + lock (GlobalConfigSync) + { + _globalConfiguration = value; + } + } + } + + #endregion Static Members + + #region Private Members + + /// + /// Gets or sets the API key based on the authentication name. + /// + /// The API key. + private IDictionary _apiKey = null; + + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// The prefix of the API key. + private IDictionary _apiKeyPrefix = null; + + private string _dateTimeFormat = ISO8601_DATETIME_FORMAT; + private string _tempFolderPath = Path.GetTempPath(); + + #endregion Private Members + + #region Constructors + + static Configuration() + { + _globalConfiguration = new GlobalConfiguration(); + } + + /// + /// Initializes a new instance of the class + /// + public Configuration() + { + UserAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{packageVersion}}/csharp{{/httpUserAgent}}"; + BasePath = "{{{basePath}}}"; + DefaultHeader = new {{^net35}}Concurrent{{/net35}}Dictionary(); + ApiKey = new {{^net35}}Concurrent{{/net35}}Dictionary(); + ApiKeyPrefix = new {{^net35}}Concurrent{{/net35}}Dictionary(); + } + + /// + /// Initializes a new instance of the class + /// + public Configuration( + IDictionary defaultHeader, + IDictionary apiKey, + IDictionary apiKeyPrefix, + string basePath = "{{{basePath}}}") : this() + { + if (string.{{^net35}}IsNullOrWhiteSpace{{/net35}}{{#net35}}IsNullOrEmpty{{/net35}}(basePath)) + throw new ArgumentException("The provided basePath is invalid.", "basePath"); + if (defaultHeader == null) + throw new ArgumentNullException("defaultHeader"); + if (apiKey == null) + throw new ArgumentNullException("apiKey"); + if (apiKeyPrefix == null) + throw new ArgumentNullException("apiKeyPrefix"); + + BasePath = basePath; + + foreach (var keyValuePair in defaultHeader) + { + DefaultHeader.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKey) + { + ApiKey.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKeyPrefix) + { + ApiKeyPrefix.Add(keyValuePair); + } + } + + /// + /// Initializes a new instance of the class with different settings + /// + /// Api client + /// Dictionary of default HTTP header + /// Username + /// Password + /// accessToken + /// Dictionary of API key + /// Dictionary of API key prefix + /// Temp folder path + /// DateTime format string + /// HTTP connection timeout (in milliseconds) + /// HTTP user agent + [Obsolete("Use explicit object construction and setting of properties.", true)] + public Configuration( + // ReSharper disable UnusedParameter.Local + ApiClient apiClient = null, + IDictionary defaultHeader = null, + string username = null, + string password = null, + string accessToken = null, + IDictionary apiKey = null, + IDictionary apiKeyPrefix = null, + string tempFolderPath = null, + string dateTimeFormat = null, + int timeout = 100000, + string userAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{packageVersion}}/csharp{{/httpUserAgent}}" + // ReSharper restore UnusedParameter.Local + ) + { + + } + + /// + /// Initializes a new instance of the Configuration class. + /// + /// Api client. + [Obsolete("This constructor caused unexpected sharing of static data. It is no longer supported.", true)] + // ReSharper disable once UnusedParameter.Local + public Configuration(ApiClient apiClient) + { + + } + + #endregion Constructors + + + #region Properties + + private ApiClient _apiClient = null; + /// + /// Gets an instance of an ApiClient for this configuration + /// + public virtual ApiClient ApiClient + { + get + { + if (_apiClient == null) _apiClient = CreateApiClient(); + return _apiClient; + } + } + + private String _basePath = null; + /// + /// Gets or sets the base path for API access. + /// + public virtual string BasePath { + get { return _basePath; } + set { + _basePath = value; + // pass-through to ApiClient if it's set. + if(_apiClient != null) { + _apiClient.RestClient.BaseUrl = new Uri(_basePath); + } + } + } + + /// + /// Gets or sets the default header. + /// + public virtual IDictionary DefaultHeader { get; set; } + + /// + /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. + /// + public virtual int Timeout + { + {{#netStandard}}get { return (int)ApiClient.RestClient.Timeout.GetValueOrDefault(TimeSpan.FromSeconds(0)).TotalMilliseconds; } + set { ApiClient.RestClient.Timeout = TimeSpan.FromMilliseconds(value); }{{/netStandard}}{{^netStandard}} + get { return ApiClient.RestClient.Timeout; } + set { ApiClient.RestClient.Timeout = value; }{{/netStandard}} + } + + /// + /// Gets or sets the HTTP user agent. + /// + /// Http user agent. + public virtual string UserAgent { get; set; } + + /// + /// Gets or sets the username (HTTP basic authentication). + /// + /// The username. + public virtual string Username { get; set; } + + /// + /// Gets or sets the password (HTTP basic authentication). + /// + /// The password. + public virtual string Password { get; set; } + + /// + /// Gets or sets the LWAAuthorizationCredentials for Amazon Selling Partner API Authorization + /// + /// The LWAAuthorizationCredentials + public virtual LWAAuthorizationCredentials AuthorizationCredentials { get; set; } + + /// + /// Gets or sets the AWSAuthenticationCredentials for Amazon Selling Partner API Authentication + /// + /// The AWSAuthenticationCredentials + public virtual AWSAuthenticationCredentials AuthenticationCredentials { get; set; } + + /// + /// Gets the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + public string GetApiKeyWithPrefix(string apiKeyIdentifier) + { + var apiKeyValue = ""; + ApiKey.TryGetValue (apiKeyIdentifier, out apiKeyValue); + var apiKeyPrefix = ""; + if (ApiKeyPrefix.TryGetValue (apiKeyIdentifier, out apiKeyPrefix)) + return apiKeyPrefix + " " + apiKeyValue; + else + return apiKeyValue; + } + + /// + /// Gets or sets the access token for OAuth2 authentication. + /// + /// The access token. + public virtual string AccessToken { get; set; } + + /// + /// Gets or sets the temporary folder path to store the files downloaded from the server. + /// + /// Folder path. + public virtual string TempFolderPath + { + get { return _tempFolderPath; } + + set + { + if (string.IsNullOrEmpty(value)) + { + // Possible breaking change since swagger-codegen 2.2.1, enforce a valid temporary path on set. + _tempFolderPath = Path.GetTempPath(); + return; + } + + // create the directory if it does not exist + if (!Directory.Exists(value)) + { + Directory.CreateDirectory(value); + } + + // check if the path contains directory separator at the end + if (value[value.Length - 1] == Path.DirectorySeparatorChar) + { + _tempFolderPath = value; + } + else + { + _tempFolderPath = value + Path.DirectorySeparatorChar; + } + } + } + + /// + /// Gets or sets the date time format used when serializing in the ApiClient + /// By default, it's set to ISO 8601 - "o", for others see: + /// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx + /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx + /// No validation is done to ensure that the string you're providing is valid + /// + /// The DateTimeFormat string + public virtual string DateTimeFormat + { + get { return _dateTimeFormat; } + set + { + if (string.IsNullOrEmpty(value)) + { + // Never allow a blank or null string, go back to the default + _dateTimeFormat = ISO8601_DATETIME_FORMAT; + return; + } + + // Caution, no validation when you choose date time format other than ISO 8601 + // Take a look at the above links + _dateTimeFormat = value; + } + } + + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// The prefix of the API key. + public virtual IDictionary ApiKeyPrefix + { + get { return _apiKeyPrefix; } + set + { + if (value == null) + { + throw new InvalidOperationException("ApiKeyPrefix collection may not be null."); + } + _apiKeyPrefix = value; + } + } + + /// + /// Gets or sets the API key based on the authentication name. + /// + /// The API key. + public virtual IDictionary ApiKey + { + get { return _apiKey; } + set + { + if (value == null) + { + throw new InvalidOperationException("ApiKey collection may not be null."); + } + _apiKey = value; + } + } + + #endregion Properties + + #region Methods + + /// + /// Add default header. + /// + /// Header field name. + /// Header field value. + /// + public void AddDefaultHeader(string key, string value) + { + DefaultHeader[key] = value; + } + + /// + /// Creates a new based on this instance. + /// + /// + public ApiClient CreateApiClient() + { + return new ApiClient(this); + } + + + /// + /// Returns a string with essential information for debugging. + /// + public static String ToDebugReport() + { + String report = "C# SDK ({{{packageName}}}) Debug Report:\n"; + {{^netStandard}} + {{^supportsUWP}} + report += " OS: " + System.Environment.OSVersion + "\n"; + report += " .NET Framework Version: " + System.Environment.Version + "\n"; + {{/supportsUWP}} + {{/netStandard}} + {{#netStandard}} + report += " OS: " + System.Runtime.InteropServices.RuntimeInformation.OSDescription + "\n"; + {{/netStandard}} + report += " Version of the API: {{{version}}}\n"; + report += " SDK Package Version: {{{packageVersion}}}\n"; + + return report; + } + + /// + /// Add Api Key Header. + /// + /// Api Key name. + /// Api Key value. + /// + public void AddApiKey(string key, string value) + { + ApiKey[key] = value; + } + + /// + /// Sets the API key prefix. + /// + /// Api Key name. + /// Api Key value. + public void AddApiKeyPrefix(string key, string value) + { + ApiKeyPrefix[key] = value; + } + + #endregion Methods + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache new file mode 100644 index 0000000..525e9ce --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/IReadableConfiguration.mustache @@ -0,0 +1,98 @@ +{{>partial_header}} + +using System.Collections.Generic; +using Amazon.SellingPartnerAPIAA; + +namespace {{packageName}}.Client +{ + /// + /// Represents a readable-only configuration contract. + /// + public interface IReadableConfiguration + { + /// + /// Gets the access token. + /// + /// Access token. + string AccessToken { get; } + + /// + /// Gets the API key. + /// + /// API key. + IDictionary ApiKey { get; } + + /// + /// Gets the API key prefix. + /// + /// API key prefix. + IDictionary ApiKeyPrefix { get; } + + /// + /// Gets the base path. + /// + /// Base path. + string BasePath { get; } + + /// + /// Gets the date time format. + /// + /// Date time foramt. + string DateTimeFormat { get; } + + /// + /// Gets the default header. + /// + /// Default header. + IDictionary DefaultHeader { get; } + + /// + /// Gets the temp folder path. + /// + /// Temp folder path. + string TempFolderPath { get; } + + /// + /// Gets the HTTP connection timeout (in milliseconds) + /// + /// HTTP connection timeout. + int Timeout { get; } + + /// + /// Gets the user agent. + /// + /// User agent. + string UserAgent { get; } + + /// + /// Gets the username. + /// + /// Username. + string Username { get; } + + /// + /// Gets the password. + /// + /// Password. + string Password { get; } + + /// + /// Gets the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + string GetApiKeyWithPrefix(string apiKeyIdentifier); + + /// + /// Gets the LWAAuthorizationCredentials for Amazon Selling Partner API Authorization + /// + /// AuthorizationCredentials + LWAAuthorizationCredentials AuthorizationCredentials { get; } + + /// + /// Gets the AWSAuthenticationCredentials for Amazon Selling Partner API Authentication + /// + /// AuthenticationCredentials + AWSAuthenticationCredentials AuthenticationCredentials { get; } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache new file mode 100644 index 0000000..a9b1bcc --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api.mustache @@ -0,0 +1,478 @@ +{{>partial_header}} +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +{{#netStandard}} +using RestSharp.Portable; +{{/netStandard}} +{{^netStandard}} +using RestSharp; +{{/netStandard}} +using {{packageName}}.Client; +{{#hasImport}}using {{packageName}}.{{modelPackage}}; +{{/hasImport}} +using Amazon.SellingPartnerAPIAA; + +namespace {{packageName}}.{{apiPackage}} +{ + {{#operations}} + /// + /// Represents a collection of functions to interact with the API endpoints + /// + {{>visibility}} interface {{interfacePrefix}}{{classname}} : IApiAccessor + { + #region Synchronous Operations + {{#operation}} + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} + {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// ApiResponse of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object(void){{/returnType}} + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/operation}} + #endregion Synchronous Operations + {{#supportsAsync}} + #region Asynchronous Operations + {{#operation}} + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// Task of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}} + {{#returnType}}System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// Task of ApiResponse{{#returnType}} ({{returnType}}){{/returnType}} + System.Threading.Tasks.Task> {{operationId}}AsyncWithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/operation}} + #endregion Asynchronous Operations + {{/supportsAsync}} + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + {{>visibility}} partial class {{classname}} : {{interfacePrefix}}{{classname}} + { + private {{packageName}}.Client.ExceptionFactory _exceptionFactory = (name, response) => null; + + /// + /// Initializes a new instance of the class + /// using Configuration object + /// + /// An instance of Configuration + /// + public {{classname}}({{packageName}}.Client.Configuration configuration) + { + this.Configuration = configuration; + ExceptionFactory = {{packageName}}.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Gets the base path of the API client. + /// + /// The base path + public String GetBasePath() + { + return this.Configuration.ApiClient.RestClient.BaseUrl.ToString(); + } + + /// + /// Sets the base path of the API client. + /// + /// The base path + [Obsolete("SetBasePath is deprecated, please do 'Configuration.ApiClient = new ApiClient(\"http://new-path\")' instead.")] + public void SetBasePath(String basePath) + { + // do nothing + } + + /// + /// Gets or sets the configuration object + /// + /// An instance of the Configuration + public {{packageName}}.Client.Configuration Configuration {get; set;} + + /// + /// Provides a factory method hook for the creation of exceptions. + /// + public {{packageName}}.Client.ExceptionFactory ExceptionFactory + { + get + { + if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1) + { + throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported."); + } + return _exceptionFactory; + } + set { _exceptionFactory = value; } + } + + /// + /// Gets the default header. + /// + /// Dictionary of HTTP header + [Obsolete("DefaultHeader is deprecated, please use Configuration.DefaultHeader instead.")] + public IDictionary DefaultHeader() + { + return new {{^net35}}ReadOnly{{/net35}}Dictionary(this.Configuration.DefaultHeader); + } + + /// + /// Add default header. + /// + /// Header field name. + /// Header field value. + /// + [Obsolete("AddDefaultHeader is deprecated, please use Configuration.AddDefaultHeader instead.")] + public void AddDefaultHeader(string key, string value) + { + this.Configuration.AddDefaultHeader(key, value); + } + + {{#operation}} + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} + public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + {{#returnType}}ApiResponse<{{{returnType}}}> localVarResponse = {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + return localVarResponse.Data;{{/returnType}}{{^returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/returnType}} + } + + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// ApiResponse of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object(void){{/returnType}} + public ApiResponse<{{#returnType}} {{{returnType}}} {{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + {{#allParams}} + {{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) + throw new ApiException(400, "Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}"); + {{/required}} + {{/allParams}} + + var localVarPath = "{{#netStandard}}.{{/netStandard}}{{{path}}}"; + var localVarPathParams = new Dictionary(); + var localVarQueryParams = new List>(); + var localVarHeaderParams = new Dictionary(this.Configuration.DefaultHeader); + var localVarFormParams = new Dictionary(); + var localVarFileParams = new Dictionary(); + Object localVarPostBody = null; + + // to determine the Content-Type header + String[] localVarHttpContentTypes = new String[] { + {{#consumes}} + "{{{mediaType}}}"{{#hasMore}}, {{/hasMore}} + {{/consumes}} + }; + String localVarHttpContentType = this.Configuration.ApiClient.SelectHeaderContentType(localVarHttpContentTypes); + + // to determine the Accept header + String[] localVarHttpHeaderAccepts = new String[] { + {{#produces}} + "{{{mediaType}}}"{{#hasMore}},{{/hasMore}} + {{/produces}} + }; + String localVarHttpHeaderAccept = this.Configuration.ApiClient.SelectHeaderAccept(localVarHttpHeaderAccepts); + if (localVarHttpHeaderAccept != null) + localVarHeaderParams.Add("Accept", localVarHttpHeaderAccept); + + {{#pathParams}} + if ({{paramName}} != null) localVarPathParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToString({{paramName}})); // path parameter + {{/pathParams}} + {{#queryParams}} + if ({{paramName}} != null) localVarQueryParams.AddRange(this.Configuration.ApiClient.ParameterToKeyValuePairs("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); // query parameter + {{/queryParams}} + {{#headerParams}} + if ({{paramName}} != null) localVarHeaderParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToString({{paramName}})); // header parameter + {{/headerParams}} + {{#formParams}} + if ({{paramName}} != null) {{#isFile}}localVarFileParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToFile("{{baseName}}", {{paramName}}));{{/isFile}}{{^isFile}}localVarFormParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToString({{paramName}})); // form parameter{{/isFile}} + {{/formParams}} + {{#bodyParam}} + if ({{paramName}} != null && {{paramName}}.GetType() != typeof(byte[])) + { + localVarPostBody = this.Configuration.ApiClient.Serialize({{paramName}}); // http body (model) parameter + } + else + { + localVarPostBody = {{paramName}}; // byte array + } + {{/bodyParam}} + + {{#authMethods}} + // authentication ({{name}}) required + {{#isApiKey}} + {{#isKeyInHeader}} + if (!String.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))) + { + localVarHeaderParams["{{keyParamName}}"] = this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"); + } + {{/isKeyInHeader}} + {{#isKeyInQuery}} + if (!String.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))) + { + localVarQueryParams.AddRange(this.Configuration.ApiClient.ParameterToKeyValuePairs("", "{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))); + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasic}} + // http basic authentication required + if (!String.IsNullOrEmpty(this.Configuration.Username) || !String.IsNullOrEmpty(this.Configuration.Password)) + { + localVarHeaderParams["Authorization"] = "Basic " + ApiClient.Base64Encode(this.Configuration.Username + ":" + this.Configuration.Password); + } + {{/isBasic}} + {{#isOAuth}} + // oauth required + if (!String.IsNullOrEmpty(this.Configuration.AccessToken)) + { + localVarHeaderParams["Authorization"] = "Bearer " + this.Configuration.AccessToken; + } + {{/isOAuth}} + {{/authMethods}} + + // make the HTTP request + IRestResponse localVarResponse = (IRestResponse) this.Configuration.ApiClient.CallApi(localVarPath, + Method.{{httpMethod}}, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarFileParams, + localVarPathParams, localVarHttpContentType); + + int localVarStatusCode = (int) localVarResponse.StatusCode; + + if (ExceptionFactory != null) + { + Exception exception = ExceptionFactory("{{operationId}}", localVarResponse); + if (exception != null) throw exception; + } + + {{#returnType}} + return new ApiResponse<{{{returnType}}}>(localVarStatusCode, + localVarResponse.Headers.ToDictionary(x => x.{{^netStandard}}Name{{/netStandard}}{{#netStandard}}Key{{/netStandard}}, x => x.Value.ToString()), + ({{{returnType}}}) this.Configuration.ApiClient.Deserialize(localVarResponse, typeof({{#returnContainer}}{{{returnContainer}}}{{/returnContainer}}{{^returnContainer}}{{{returnType}}}{{/returnContainer}}))); + {{/returnType}} + {{^returnType}} + return new ApiResponse(localVarStatusCode, + localVarResponse.Headers.ToDictionary(x => x.{{^netStandard}}Name{{/netStandard}}{{#netStandard}}Key{{/netStandard}}, x => x.Value.ToString()), + null); + {{/returnType}} + } + + {{#supportsAsync}} + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// Task of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}} + {{#returnType}}public async System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}public async System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + {{#returnType}}ApiResponse<{{{returnType}}}> localVarResponse = await {{operationId}}AsyncWithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + return localVarResponse.Data;{{/returnType}}{{^returnType}}await {{operationId}}AsyncWithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/returnType}} + + } + + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}}/// Task of ApiResponse{{#returnType}} ({{returnType}}){{/returnType}} + public async System.Threading.Tasks.Task> {{operationId}}AsyncWithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + {{#allParams}} + {{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) + throw new ApiException(400, "Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}"); + {{/required}} + {{/allParams}} + + var localVarPath = "{{#netStandard}}.{{/netStandard}}{{{path}}}"; + var localVarPathParams = new Dictionary(); + var localVarQueryParams = new List>(); + var localVarHeaderParams = new Dictionary(this.Configuration.DefaultHeader); + var localVarFormParams = new Dictionary(); + var localVarFileParams = new Dictionary(); + Object localVarPostBody = null; + + // to determine the Content-Type header + String[] localVarHttpContentTypes = new String[] { + {{#consumes}} + "{{{mediaType}}}"{{#hasMore}}, {{/hasMore}} + {{/consumes}} + }; + String localVarHttpContentType = this.Configuration.ApiClient.SelectHeaderContentType(localVarHttpContentTypes); + + // to determine the Accept header + String[] localVarHttpHeaderAccepts = new String[] { + {{#produces}} + "{{{mediaType}}}"{{#hasMore}},{{/hasMore}} + {{/produces}} + }; + String localVarHttpHeaderAccept = this.Configuration.ApiClient.SelectHeaderAccept(localVarHttpHeaderAccepts); + if (localVarHttpHeaderAccept != null) + localVarHeaderParams.Add("Accept", localVarHttpHeaderAccept); + + {{#pathParams}} + if ({{paramName}} != null) localVarPathParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToString({{paramName}})); // path parameter + {{/pathParams}} + {{#queryParams}} + if ({{paramName}} != null) localVarQueryParams.AddRange(this.Configuration.ApiClient.ParameterToKeyValuePairs("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); // query parameter + {{/queryParams}} + {{#headerParams}} + if ({{paramName}} != null) localVarHeaderParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToString({{paramName}})); // header parameter + {{/headerParams}} + {{#formParams}} + if ({{paramName}} != null) {{#isFile}}localVarFileParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToFile("{{baseName}}", {{paramName}}));{{/isFile}}{{^isFile}}localVarFormParams.Add("{{baseName}}", this.Configuration.ApiClient.ParameterToString({{paramName}})); // form parameter{{/isFile}} + {{/formParams}} + {{#bodyParam}} + if ({{paramName}} != null && {{paramName}}.GetType() != typeof(byte[])) + { + localVarPostBody = this.Configuration.ApiClient.Serialize({{paramName}}); // http body (model) parameter + } + else + { + localVarPostBody = {{paramName}}; // byte array + } + {{/bodyParam}} + + {{#authMethods}} + // authentication ({{name}}) required + {{#isApiKey}} + {{#isKeyInHeader}} + if (!String.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))) + { + localVarHeaderParams["{{keyParamName}}"] = this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"); + } + {{/isKeyInHeader}} + {{#isKeyInQuery}} + if (!String.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))) + { + localVarQueryParams.AddRange(this.Configuration.ApiClient.ParameterToKeyValuePairs("", "{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))); + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasic}} + // http basic authentication required + if (!String.IsNullOrEmpty(this.Configuration.Username) || !String.IsNullOrEmpty(this.Configuration.Password)) + { + localVarHeaderParams["Authorization"] = "Basic " + ApiClient.Base64Encode(this.Configuration.Username + ":" + this.Configuration.Password); + } + {{/isBasic}} + {{#isOAuth}} + // oauth required + if (!String.IsNullOrEmpty(this.Configuration.AccessToken)) + { + localVarHeaderParams["Authorization"] = "Bearer " + this.Configuration.AccessToken; + } + {{/isOAuth}} + {{/authMethods}} + + // make the HTTP request + IRestResponse localVarResponse = (IRestResponse) await this.Configuration.ApiClient.CallApiAsync(localVarPath, + Method.{{httpMethod}}, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarFileParams, + localVarPathParams, localVarHttpContentType); + + int localVarStatusCode = (int) localVarResponse.StatusCode; + + if (ExceptionFactory != null) + { + Exception exception = ExceptionFactory("{{operationId}}", localVarResponse); + if (exception != null) throw exception; + } + + {{#returnType}} + return new ApiResponse<{{{returnType}}}>(localVarStatusCode, + localVarResponse.Headers.ToDictionary(x => x.{{^netStandard}}Name{{/netStandard}}{{#netStandard}}Key{{/netStandard}}, x => x.Value.ToString()), + ({{{returnType}}}) this.Configuration.ApiClient.Deserialize(localVarResponse, typeof({{#returnContainer}}{{{returnContainer}}}{{/returnContainer}}{{^returnContainer}}{{{returnType}}}{{/returnContainer}}))); + {{/returnType}} + {{^returnType}} + return new ApiResponse(localVarStatusCode, + localVarResponse.Headers.ToDictionary(x => x.{{^netStandard}}Name{{/netStandard}}{{#netStandard}}Key{{/netStandard}}, x => x.Value.ToString()), + null); + {{/returnType}} + } + + {{/supportsAsync}} + {{/operation}} + + public class Builder + { + private LWAAuthorizationCredentials lwaAuthorizationCredentials; + private AWSAuthenticationCredentials awsAuthenticationCredentials; + + public Builder SetLWAAuthorizationCredentials(LWAAuthorizationCredentials lwaAuthorizationCredentials) + { + this.lwaAuthorizationCredentials = lwaAuthorizationCredentials; + return this; + } + + public Builder SetAWSAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) + { + this.awsAuthenticationCredentials = awsAuthenticationCredentials; + return this; + } + + public {{classname}} Build() + { + if (lwaAuthorizationCredentials == null) + { + throw new NullReferenceException("LWAAuthoriztionCredentials not set"); + } + + if (awsAuthenticationCredentials == null) + { + throw new NullReferenceException("AWSAuthenticationCredentials not set"); + } + + {{packageName}}.Client.Configuration configuration = new {{packageName}}.Client.Configuration() + { + AuthorizationCredentials = lwaAuthorizationCredentials, + AuthenticationCredentials = awsAuthenticationCredentials + }; + + // default HTTP connection timeout (in milliseconds) + configuration.Timeout = 100000; + + return new {{classname}}(configuration); + } + } + } + {{/operations}} +} diff --git a/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api_test.mustache b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api_test.mustache new file mode 100644 index 0000000..2227fea --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/src/Amazon.SellingPartnerAPIAA/resources/swagger-codegen/templates/api_test.mustache @@ -0,0 +1,76 @@ +{{>partial_header}} +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using RestSharp; +using NUnit.Framework; + +using {{packageName}}.Client; +using {{packageName}}.{{apiPackage}}; +{{#hasImport}}using {{packageName}}.{{modelPackage}}; +{{/hasImport}} + +namespace {{packageName}}.Test +{ + /// + /// Class for testing {{classname}} + /// + /// + /// This file is automatically generated by Swagger Codegen. + /// Please update the test case below to test the API endpoint. + /// + [TestFixture] + public class {{classname}}Tests + { + private {{classname}} instance; + + /// + /// Setup before each unit test + /// + [SetUp] + public void Init() + { + // TODO uncomment below to initialize instance for testing + //instance = new {{classname}}(); + } + + /// + /// Clean up after each unit test + /// + [TearDown] + public void Cleanup() + { + + } + + /// + /// Test an instance of {{classname}} + /// + [Test] + public void {{operationId}}InstanceTest() + { + // TODO uncomment below to test 'IsInstanceOfType' {{classname}} + //Assert.IsInstanceOfType(typeof({{classname}}), instance, "instance is a {{classname}}"); + } + + {{#operations}}{{#operation}} + /// + /// Test {{operationId}} + /// + [Test] + public void {{operationId}}Test() + { + // TODO uncomment below to test the method and replace null with proper value + {{#allParams}} + //{{{dataType}}} {{paramName}} = null; + {{/allParams}} + //{{#returnType}}var response = {{/returnType}}instance.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{#returnType}}//Assert.IsInstanceOf<{{{returnType}}}> (response, "response is {{{returnType}}}");{{/returnType}} + } + {{/operation}}{{/operations}} + } + +} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs new file mode 100644 index 0000000..d0487a0 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSigV4SignerTest.cs @@ -0,0 +1,88 @@ +using Moq; +using Xunit; +using RestSharp; +using Amazon.SellingPartnerAPIAA; +using System; + +namespace Amazon.SellingPartnerAPIAATests +{ + public class AWSSigV4SignerTest + { + private const string TestAccessKeyId = "aKey"; + private const string TestSecretKey = "sKey"; + private const string TestRegion = "us-east-1"; + private const string TestResourcePath = "iam/user"; + private const string TestHost = "sellingpartnerapi.amazon.com"; + + private RestRequest request; + private AWSSigV4Signer sigV4SignerUnderTest; + private Mock mockAWSSignerHelper; + + public AWSSigV4SignerTest() + { + request = new RestRequest(TestResourcePath, Method.GET); + + AWSAuthenticationCredentials authenticationCredentials = new AWSAuthenticationCredentials + { + AccessKeyId = TestAccessKeyId, + SecretKey = TestSecretKey, + Region = TestRegion + }; + mockAWSSignerHelper = new Mock(); + sigV4SignerUnderTest = new AWSSigV4Signer(authenticationCredentials); + sigV4SignerUnderTest.AwsSignerHelper = mockAWSSignerHelper.Object; + } + + [Fact] + public void TestSignRequest() + { + DateTime signingDate = DateTime.UtcNow; + string expectedHashedCanonicalRequest = "b7a5ea4c3179fcebed77f19ccd7d85795d4b7a1810709b55fa7ad3fd79ab6adc"; + string expectedSignedHeaders = "testSignedHeaders"; + string expectedSignature = "testSignature"; + string expectedStringToSign = "testStringToSign"; + mockAWSSignerHelper.Setup(signerHelper => signerHelper.InitializeHeaders(request, TestHost)) + .Returns(signingDate); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractCanonicalURIParameters(request.Resource)) + .Returns("testURIParameters"); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractCanonicalQueryString(request)) + .Returns("testCanonicalQueryString"); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractCanonicalHeaders(request)) + .Returns("testCanonicalHeaders"); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.ExtractSignedHeaders(request)) + .Returns(expectedSignedHeaders); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.HashRequestBody(request)) + .Returns("testHashRequestBody"); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.BuildStringToSign(signingDate, + expectedHashedCanonicalRequest, TestRegion)) + .Returns(expectedStringToSign); + mockAWSSignerHelper.Setup(signerHelper => signerHelper.CalculateSignature(expectedStringToSign, + signingDate, TestSecretKey, TestRegion)) + .Returns(expectedSignature); + + IRestRequest actualRestRequest = sigV4SignerUnderTest.Sign(request, TestHost); + + mockAWSSignerHelper.Verify(signerHelper => signerHelper.InitializeHeaders(request, TestHost)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractCanonicalURIParameters(request.Resource)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractCanonicalQueryString(request)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractCanonicalHeaders(request)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.ExtractSignedHeaders(request)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.HashRequestBody(request)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.BuildStringToSign(signingDate, + expectedHashedCanonicalRequest, + TestRegion)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.CalculateSignature(expectedStringToSign, + signingDate, + TestSecretKey, + TestRegion)); + mockAWSSignerHelper.Verify(signerHelper => signerHelper.AddSignature(request, + TestAccessKeyId, + expectedSignedHeaders, + expectedSignature, + TestRegion, + signingDate)); + + Assert.Equal(request, actualRestRequest); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs new file mode 100644 index 0000000..4a10c8c --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/AWSSignerHelperTest.cs @@ -0,0 +1,269 @@ +using System; +using Xunit; +using RestSharp; +using Amazon.SellingPartnerAPIAA; +using System.Text; +using Moq; + +namespace Amazon.SellingPartnerAPIAATests +{ + public class AWSSignerHelperTest + { + private const string Slash = "/"; + private const string ISOSigningDateTime = "20200504T121212Z"; + private const string ISOSigningDate = "20200504"; + private const string TestAccessKeyId = "aKey"; + private const string TestSecretKey = "sKey"; + private const string TestRegion = "us-east-1"; + private const string TestResourcePath = "iam/user"; + private const string TestHost = "sellingpartnerapi.amazon.com"; + private const string JsonMediaType = "application/json; charset=utf-8"; + + private static readonly DateTime SigningDate = DateTime.Parse("2020-05-04 12:12:12"); + + private AWSSignerHelper awsSignerHelperUnderTest; + + public AWSSignerHelperTest() + { + var mockDateHelper = new Mock(); + mockDateHelper.Setup(dateHelper => dateHelper.GetUtcNow()).Returns(SigningDate); + awsSignerHelperUnderTest = new AWSSignerHelper() { DateHelper = mockDateHelper.Object }; + } + + [Fact] + public void TestExtractCanonicalURIParameters() + { + IRestRequest request = new RestRequest(TestResourcePath, Method.GET); + string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(request.Resource); + Assert.Equal("/iam/user", result); + } + + [Fact] + public void TestExtractCanonicalURIParameters_ResourcePathWithSpace() + { + string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters("iam/ user"); + Assert.Equal("/iam/%2520user", result); + } + + [Fact] + public void TestExtractCanonicalURIParameters_EmptyResourcePath() + { + string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(string.Empty); + Assert.Equal(Slash, result); + } + + [Fact] + public void TestExtractCanonicalURIParameters_NullResourcePath() + { + string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(null); + Assert.Equal(Slash, result); + } + + [Fact] + public void TestExtractCanonicalURIParameters_SlashPath() + { + string result = awsSignerHelperUnderTest.ExtractCanonicalURIParameters(Slash); + Assert.Equal(Slash, result); + } + + [Fact] + public void TestExtractCanonicalQueryString() + { + IRestRequest request = new RestRequest(); + request.AddQueryParameter("Version", "2010-05-08"); + request.AddQueryParameter("Action", "ListUsers"); + request.AddQueryParameter("RequestId", "1"); + + string result = awsSignerHelperUnderTest.ExtractCanonicalQueryString(request); + //Query parameters in canonical order + Assert.Equal("Action=ListUsers&RequestId=1&Version=2010-05-08", result); + } + + [Fact] + public void TestExtractCanonicalQueryString_EmptyQueryParameters() + { + string result = awsSignerHelperUnderTest.ExtractCanonicalQueryString(new RestRequest()); + Assert.Empty(result); + } + + [Fact] + public void TestExtractCanonicalQueryString_WithUrlEncoding() + { + IRestRequest request = new RestRequest(); + request.AddQueryParameter("Action^", "ListUsers$Roles"); + string result = awsSignerHelperUnderTest.ExtractCanonicalQueryString(request); + Assert.Equal("Action%5E=ListUsers%24Roles", result); + } + + [Fact] + public void TestExtractCanonicalHeaders() + { + IRestRequest request = new RestRequest(); + request.AddHeader("X-Amz-Date", "20150830T123600Z"); + request.AddHeader("Host", "iam.amazonaws.com"); + request.AddHeader("Content-Type", JsonMediaType); + + string result = awsSignerHelperUnderTest.ExtractCanonicalHeaders(request); + Assert.Equal("content-type:application/json; charset=utf-8\nhost:iam.amazonaws.com\n" + + "x-amz-date:20150830T123600Z\n", result); + } + + [Fact] + public void TestExtractCanonicalHeaders_NoHeader() + { + string result = awsSignerHelperUnderTest.ExtractCanonicalHeaders(new RestRequest()); + Assert.Empty(result); + } + + [Fact] + public void TestExtractSignedHeaders() + { + IRestRequest request = new RestRequest(); + request.AddHeader("X-Amz-Date", "20150830T123600Z"); + request.AddHeader("Host", "iam.amazonaws.com"); + request.AddHeader("Content-Type", JsonMediaType); + + string result = awsSignerHelperUnderTest.ExtractSignedHeaders(request); + Assert.Equal("content-type;host;x-amz-date", result); + } + + [Fact] + public void TestExtractSignedHeaders_NoHeader() + { + string result = awsSignerHelperUnderTest.ExtractSignedHeaders(new RestRequest()); + Assert.Empty(result); + } + + [Fact] + public void TestHashRequestBody() + { + IRestRequest request = new RestRequest(TestResourcePath, Method.POST); + request.AddJsonBody("{\"test\":\"payload\"}"); + + string result = awsSignerHelperUnderTest.HashRequestBody(request); + Assert.NotEmpty(result); + } + + [Fact] + public void TestHashRequestBody_NoBody() + { + string result = awsSignerHelperUnderTest.HashRequestBody(new RestRequest()); + Assert.NotEmpty(result); + } + + [Fact] + public void TestBuildStringToSign() + { + string expectedCanonicalHash = "foo"; + StringBuilder expectedStringBuilder = new StringBuilder(); + expectedStringBuilder.AppendLine("AWS4-HMAC-SHA256"); + expectedStringBuilder.AppendLine(ISOSigningDateTime); + expectedStringBuilder.AppendFormat("{0}/{1}/execute-api/aws4_request\n", ISOSigningDate, TestRegion); + expectedStringBuilder.Append(expectedCanonicalHash); + + string result = awsSignerHelperUnderTest.BuildStringToSign(SigningDate, expectedCanonicalHash, TestRegion); + + Assert.Equal(expectedStringBuilder.ToString(), result); + } + + [Fact] + public void TestInitializeHeadersReturnsUtcNow() + { + Assert.Equal(SigningDate, awsSignerHelperUnderTest.InitializeHeaders(new RestRequest(), TestHost)); + } + + [Fact] + public void TestInitializeHeadersSetsUtcNowXAmzDateHeader() + { + IRestRequest request = new RestRequest(); + awsSignerHelperUnderTest.InitializeHeaders(request, TestHost); + + Parameter actualParameter = request.Parameters.Find(parameter => + ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.XAmzDateHeaderName); + + Assert.Equal(ISOSigningDateTime, actualParameter.Value); + } + + [Fact] + public void TestInitializeHeadersOverwritesXAmzDateHeader() + { + IRestRequest request = new RestRequest(); + request.AddHeader(AWSSignerHelper.XAmzDateHeaderName, "foobar"); + + awsSignerHelperUnderTest.InitializeHeaders(request, TestHost); + + Parameter actualParameter = request.Parameters.Find(parameter => + ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.XAmzDateHeaderName); + + Assert.Equal(ISOSigningDateTime, actualParameter.Value); + } + + [Fact] + public void TestAddSignatureToRequest() + { + IRestRequest restRequest = new RestRequest(); + string expectedAccessKeyId = TestAccessKeyId; + string expectedRegion = TestRegion; + string expectedSignature = "testCalculatedSignature"; + string expectedSignedHeaders = "header1;header2"; + + string expectedAuthorizationHeaderValue = string.Format("AWS4-HMAC-SHA256 " + + "Credential={0}/{1}/{2}/execute-api/aws4_request, SignedHeaders={3}, Signature={4}", + expectedAccessKeyId, + ISOSigningDate, + expectedRegion, + expectedSignedHeaders, + expectedSignature); + + awsSignerHelperUnderTest.AddSignature(restRequest, + expectedAccessKeyId, + expectedSignedHeaders, + expectedSignature, + expectedRegion, + SigningDate); + + Parameter actualParameter = restRequest.Parameters.Find(parameter => + ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.AuthorizationHeaderName); + + Assert.Equal(expectedAuthorizationHeaderValue, actualParameter.Value); + } + + [Fact] + public void TestCalculateSignature() + { + string signature = awsSignerHelperUnderTest.CalculateSignature("testString", + SigningDate, + TestSecretKey, + TestRegion); + Assert.Equal("7e2c7c2e330123ef7468b41d8ddaf3841e6ef56959b9116b44ded5466cf96405", signature); + } + + [Fact] + public void TestInitializeHeadersSetsHostHeader() + { + IRestRequest restRequest = new RestRequest(); + + awsSignerHelperUnderTest.InitializeHeaders(restRequest, TestHost); + + Parameter actualParamter = restRequest.Parameters.Find(parameter => + ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.HostHeaderName); + + Assert.Equal(TestHost, actualParamter.Value); + } + + [Fact] + public void TestInitializeHeadersOverwritesHostHeader() + { + IRestRequest restRequest = new RestRequest(); + + restRequest.AddHeader(AWSSignerHelper.HostHeaderName, "foobar"); + + awsSignerHelperUnderTest.InitializeHeaders(restRequest, TestHost); + + Parameter actualParamter = restRequest.Parameters.Find(parameter => + ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == AWSSignerHelper.HostHeaderName); + + Assert.Equal(TestHost, actualParamter.Value); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj new file mode 100644 index 0000000..4a0bd06 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/Amazon.SellingPartnerAPIAATests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAccessTokenRequestMetaBuilderTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAccessTokenRequestMetaBuilderTest.cs new file mode 100644 index 0000000..1c96f64 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAccessTokenRequestMetaBuilderTest.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Amazon.SellingPartnerAPIAA; +using Xunit; + +namespace Amazon.SellingPartnerAPIAATests +{ + public class LWAAccessTokenRequestMetaBuilderTest + { + private const string TestClientId = "cid"; + private const string TestClientSecret = "csecret"; + private const string TestRefreshToken = "rtoken"; + private static readonly Uri TestUri = new Uri("https://www.amazon.com"); + private LWAAccessTokenRequestMetaBuilder lwaAccessTokenRequestMetaBuilderUnderTest; + + public LWAAccessTokenRequestMetaBuilderTest() + { + lwaAccessTokenRequestMetaBuilderUnderTest = new LWAAccessTokenRequestMetaBuilder(); + } + + [Fact] + public void LWAAuthorizationCredentialsWithoutScopesBuildsSellerTokenRequestMeta() + { + LWAAuthorizationCredentials lwaAuthorizationCredentials = new LWAAuthorizationCredentials() + { + ClientId = TestClientId, + ClientSecret = TestClientSecret, + Endpoint = TestUri, + RefreshToken = TestRefreshToken + }; + + LWAAccessTokenRequestMeta expected = new LWAAccessTokenRequestMeta() + { + ClientId = TestClientId, + ClientSecret = TestClientSecret, + GrantType = LWAAccessTokenRequestMetaBuilder.SellerAPIGrantType, + RefreshToken = TestRefreshToken, + Scope = null + }; + + LWAAccessTokenRequestMeta actual = lwaAccessTokenRequestMetaBuilderUnderTest.Build(lwaAuthorizationCredentials); + + Assert.Equal(expected, actual); + } + + [Fact] + public void LWAAuthorizationCredentialsWithScopesBuildsSellerlessTokenRequestMeta() + { + LWAAuthorizationCredentials lwaAuthorizationCredentials = new LWAAuthorizationCredentials() + { + ClientId = TestClientId, + ClientSecret = TestClientSecret, + Endpoint = TestUri, + Scopes = new List() { ScopeConstants.ScopeMigrationAPI, ScopeConstants.ScopeNotificationsAPI } + }; + + LWAAccessTokenRequestMeta expected = new LWAAccessTokenRequestMeta() + { + ClientId = TestClientId, + ClientSecret = TestClientSecret, + GrantType = LWAAccessTokenRequestMetaBuilder.SellerlessAPIGrantType, + Scope = string.Format("{0} {1}", ScopeConstants.ScopeMigrationAPI, ScopeConstants.ScopeNotificationsAPI), + RefreshToken = null + }; + + LWAAccessTokenRequestMeta actual = lwaAccessTokenRequestMetaBuilderUnderTest.Build(lwaAuthorizationCredentials); + + Assert.Equal(expected, actual); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAuthorizationSignerTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAuthorizationSignerTest.cs new file mode 100644 index 0000000..9a71b19 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAAuthorizationSignerTest.cs @@ -0,0 +1,49 @@ +using System; +using Xunit; +using Moq; +using RestSharp; +using Amazon.SellingPartnerAPIAA; + +namespace Amazon.SellingPartnerAPIAATests +{ + public class LWAAuthorizationSignerTest + { + private static readonly LWAAuthorizationCredentials LWAAuthorizationCredentials = new LWAAuthorizationCredentials() + { + ClientId = "cid", + ClientSecret = "csecret", + Endpoint = new Uri("https://www.amazon.com") + }; + + private LWAAuthorizationSigner lwaAuthorizationSignerUnderTest; + + public LWAAuthorizationSignerTest() + { + lwaAuthorizationSignerUnderTest = new LWAAuthorizationSigner(LWAAuthorizationCredentials); + } + + [Fact] + public void ConstructorInitializesLWAClientWithCredentials() + { + Assert.Equal(LWAAuthorizationCredentials, lwaAuthorizationSignerUnderTest.LWAClient.LWAAuthorizationCredentials); + } + + [Fact] + public void RequestIsSignedFromLWAClientProvidedToken() + { + string expectedAccessToken = "foo"; + + var mockLWAClient = new Mock(LWAAuthorizationCredentials); + mockLWAClient.Setup(lwaClient => lwaClient.GetAccessToken()).Returns(expectedAccessToken); + lwaAuthorizationSignerUnderTest.LWAClient = mockLWAClient.Object; + + IRestRequest restRequest = new RestRequest(); + restRequest = lwaAuthorizationSignerUnderTest.Sign(restRequest); + + Parameter actualAccessTokenHeader = restRequest.Parameters.Find(parameter => + ParameterType.HttpHeader.Equals(parameter.Type) && parameter.Name == LWAAuthorizationSigner.AccessTokenHeaderName); + + Assert.Equal(expectedAccessToken, actualAccessTokenHeader.Value); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs new file mode 100644 index 0000000..cf4fbc2 --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/LWAClientTest.cs @@ -0,0 +1,148 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using Moq; +using Newtonsoft.Json.Linq; +using RestSharp; +using Amazon.SellingPartnerAPIAA; +using Xunit; + +namespace Amazon.SellingPartnerAPIAATests +{ + public class LWAClientTest + { + private const string TestClientSecret = "cSecret"; + private const string TestClientId = "cId"; + private const string TestRefreshGrantType = "rToken"; + private Mock mockRestClient; + private Mock mockLWAAccessTokenRequestMetaBuilder; + + private static readonly Uri TestEndpoint = new Uri("https://www.amazon.com/lwa"); + private static readonly LWAAuthorizationCredentials LWAAuthorizationCredentials = new LWAAuthorizationCredentials + { + ClientId = TestClientId, + ClientSecret = TestClientSecret, + RefreshToken = TestRefreshGrantType, + Endpoint = TestEndpoint + }; + private static readonly IRestResponse Response = new RestResponse + { + StatusCode = HttpStatusCode.OK, + ResponseStatus = ResponseStatus.Completed, + Content = @"{access_token:'Azta|foo'}" + }; + + public LWAClientTest() + { + mockRestClient = new Mock(); + mockLWAAccessTokenRequestMetaBuilder = new Mock(); + } + + [Fact] + public void InitializeLWAAuthorizationCredentials() + { + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + Assert.Equal(LWAAuthorizationCredentials, lwaClientUnderTest.LWAAuthorizationCredentials); + } + + [Fact] + public void MakeRequestFromMeta() + { + IRestRequest request = new RestRequest(); + LWAAccessTokenRequestMeta expectedLWAAccessTokenRequestMeta = new LWAAccessTokenRequestMeta() + { + ClientSecret = "expectedSecret", + ClientId = "expectedClientId", + RefreshToken = "expectedRefreshToken", + GrantType = "expectedGrantType" + }; + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = req; }) + .Returns(Response); + + mockLWAAccessTokenRequestMetaBuilder.Setup(builder => builder.Build(LWAAuthorizationCredentials)) + .Returns(expectedLWAAccessTokenRequestMeta); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + lwaClientUnderTest.LWAAccessTokenRequestMetaBuilder = mockLWAAccessTokenRequestMetaBuilder.Object; + lwaClientUnderTest.GetAccessToken(); + + Parameter requestBody = request.Parameters + .FirstOrDefault(parameter => parameter.Type.Equals(ParameterType.RequestBody)); + + JObject jsonRequestBody = JObject.Parse(requestBody.Value.ToString()); + + Assert.Equal(Method.POST, request.Method); + Assert.Equal(TestEndpoint.AbsolutePath, request.Resource); + Assert.Equal(expectedLWAAccessTokenRequestMeta.RefreshToken, jsonRequestBody.GetValue("refresh_token")); + Assert.Equal(expectedLWAAccessTokenRequestMeta.GrantType, jsonRequestBody.GetValue("grant_type")); + Assert.Equal(expectedLWAAccessTokenRequestMeta.ClientId, jsonRequestBody.GetValue("client_id")); + Assert.Equal(expectedLWAAccessTokenRequestMeta.ClientSecret, jsonRequestBody.GetValue("client_secret")); + } + + [Fact] + public void ReturnAccessTokenFromResponse() + { + IRestRequest request = new RestRequest(); + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = req; }) + .Returns(Response); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + + string accessToken = lwaClientUnderTest.GetAccessToken(); + + Assert.Equal("Azta|foo", accessToken); + } + + [Fact] + public void UnsuccessfulPostThrowsException() + { + IRestResponse response = new RestResponse + { + StatusCode = HttpStatusCode.BadRequest, + ResponseStatus = ResponseStatus.Completed, + Content = string.Empty + }; + + IRestRequest request = new RestRequest(); + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = req; }) + .Returns(response); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + + SystemException systemException = Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); + Assert.IsType(systemException.GetBaseException()); + } + + [Fact] + public void MissingAccessTokenInResponseThrowsException() + { + IRestResponse response = new RestResponse + { + StatusCode = HttpStatusCode.OK, + ResponseStatus = ResponseStatus.Completed, + Content = string.Empty + }; + + IRestRequest request = new RestRequest(); + + mockRestClient.Setup(client => client.Execute(It.IsAny())) + .Callback((IRestRequest req) => { request = (RestRequest)req; }) + .Returns(response); + + LWAClient lwaClientUnderTest = new LWAClient(LWAAuthorizationCredentials); + lwaClientUnderTest.RestClient = mockRestClient.Object; + + Assert.Throws(() => lwaClientUnderTest.GetAccessToken()); + } + } +} diff --git a/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/UtilsTest.cs b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/UtilsTest.cs new file mode 100644 index 0000000..448092a --- /dev/null +++ b/clients/sellingpartner-api-aa-csharp/test/Amazon.SellingPartnerAPIAATests/UtilsTest.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Text; +using Xunit; +using Amazon.SellingPartnerAPIAA; + +namespace Amazon.SellingPartnerAPIAATests +{ + public class UtilsTest + { + private const string TestString = "test"; + + [Fact] + public void TestUrlEncode_WithoutEncoding() + { + string result = Utils.UrlEncode("Test-_.~"); + Assert.Equal("Test-_.~", result); + } + + [Fact] + public void TestUrlEncode_WithEncoding() + { + string result = Utils.UrlEncode("Test$%*^"); + Assert.Equal("Test%24%25%2A%5E", result); + } + + [Fact] + public void TestUrlEncode_Empty() + { + Assert.Empty(Utils.UrlEncode(string.Empty)); + } + + [Fact] + public void TestHash() + { + Assert.NotEmpty(Utils.Hash(TestString)); + } + + [Fact] + public void TestToHex() + { + string result = Utils.ToHex(Encoding.UTF8.GetBytes(TestString)); + Assert.Equal("74657374", result); + } + + [Fact] + public void TestGetKeyedHash() + { + byte[] expectedHash = new byte[] { 106, 120, 238, 51, 86, 30, 87, 173, 232, 197, 95, 132,155, + 183, 80, 81, 25, 213, 212, 241, 218, 201, 168, 17, 253, 143, 54, 226, 42, 118, 61, 54 }; + byte[] keyedHash = Utils.GetKeyedHash(Encoding.UTF8.GetBytes("testKey"), TestString); + Assert.True(expectedHash.SequenceEqual(keyedHash)); + } + } +} diff --git a/clients/sellingpartner-api-aa-java/.gitignore b/clients/sellingpartner-api-aa-java/.gitignore new file mode 100644 index 0000000..8b06d03 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/.gitignore @@ -0,0 +1,9 @@ +*.iml +.idea/ +target/ +out/ +.settings/ +.DS_Store +.classpath +.project + diff --git a/clients/sellingpartner-api-aa-java/README.md b/clients/sellingpartner-api-aa-java/README.md new file mode 100644 index 0000000..0f851be --- /dev/null +++ b/clients/sellingpartner-api-aa-java/README.md @@ -0,0 +1,90 @@ +# Selling Partner API Authentication/Authorization Library +This library provides helper classes for use when signing HTTP requests for Amazon Selling Partner APIs. It is intended for use +with the Selling Partner API Client Libraries generated by [swagger codegen](https://swagger.io/tools/swagger-codegen/) +using the OkHttp library. It can also be integrated into custom projects. + +## LWAAuthorizationSigner +Obtains and signs a request with an access token from LWA (Login with Amazon) for the specified endpoint using the provided LWA credentials. + +*Example* +``` +com.squareup.okhttp.Request request = new Request.Builder() + .url(...) + ... + .build(); + +// Seller APIs + +LWAAuthorizationCredentials lwaAuthorizationCredentials = LWAAuthorizationCredentials.builder() + .clientId("...") + .clientSecret("...") + .refreshToken("...") + .endpoint("...") + .build(); + +/* Sellerless APIs +The Selling Partner API scopes can be retrieved from the ScopeConstants class and passed as argument(s) to either the withScope(String scope) or withScopes(String... scopes) method during lwaAuthorizationCredentials object instantiation. */ + +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API; + +LWAAuthorizationCredentials lwaAuthorizationCredentials = LWAAuthorizationCredentials.builder() + .clientId("...") + .clientSecret("...") + .withScopes("...") + .endpoint("...") + .build(); + +com.squareup.okhttp.Request signedRequest = new LWAAuthorizationSigner(lwaAuthorizationCredentials) + .sign(request); +``` + +## AWSSigV4Signer +Signs a request with [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) +using the provided AWS developer account credentials. + +*Example* +``` +com.squareup.okhttp.Request request = new Request.Builder() + .url(...) + ... + .build(); + +AWSAuthenticationCredentials awsAuthenticationCredentials = AWSAuthenticationCredentials.builder() + .accessKeyId("...") + .secretKey("...") + .region("...") + .build(); + +com.squareup.okhttp.Request signedRequest = new AWSSigV4Signer(awsAuthenticationCredentials) + .sign(request); +``` + +## Resources +This package features Mustache templates designed for use with [swagger codegen](https://swagger.io/tools/swagger-codegen/). +When you build Selling Partner API Swagger models with these templates, they help generate a rich SDK with functionality to invoke Selling Partner APIs built in. The templates are located in *resources/swagger-codegen*. + +## Building +This library can be built using Maven by running this command in the package root: + ``` +mvn clean package +``` +Dependencies are declared in the pom.xml file. + +## License +Swagger Codegen templates are subject to the [Swagger Codegen License](https://github.com/swagger-api/swagger-codegen#license). + +All other work licensed as follows: + +Copyright 2019 Amazon.com, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this library except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/clients/sellingpartner-api-aa-java/pom.xml b/clients/sellingpartner-api-aa-java/pom.xml new file mode 100644 index 0000000..5ce406c --- /dev/null +++ b/clients/sellingpartner-api-aa-java/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + com.amazon.sellingpartnerapi + sellingpartnerapi-aa-java + jar + 1.0 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.1 + + + build-assembly + package + + single + + + + + + jar-with-dependencies + + + + + src + target + ${project.artifactId}-${project.version} + + + + + com.amazonaws + aws-java-sdk-signer + 1.11.610 + + + + org.projectlombok + lombok + 1.18.8 + provided + + + + com.google.code.gson + gson + 2.8.5 + + + + com.squareup.okhttp + okhttp + 2.7.5 + + + + org.junit.jupiter + junit-jupiter-engine + 5.0.0 + test + + + + org.junit.jupiter + junit-jupiter-params + 5.3.2 + test + + + + + org.junit.jupiter + junit-jupiter-migrationsupport + 5.5.1 + test + + + + org.mockito + mockito-core + 3.0.0 + test + + + + org.apache.commons + commons-lang3 + 3.9 + + + + org.apache.httpcomponents + httpclient + 4.5.9 + + + diff --git a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache new file mode 100644 index 0000000..2ea2c54 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache @@ -0,0 +1,1258 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import com.squareup.okhttp.*; +import com.squareup.okhttp.internal.http.HttpMethod; +import com.squareup.okhttp.logging.HttpLoggingInterceptor; +import com.squareup.okhttp.logging.HttpLoggingInterceptor.Level; +import okio.BufferedSink; +import okio.Okio; +{{#joda}} +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +{{/joda}} +{{#threetenbp}} +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; +{{/threetenbp}} + +import javax.net.ssl.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.text.DateFormat; +{{#java8}} +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +{{/java8}} +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import {{invokerPackage}}.auth.Authentication; +import {{invokerPackage}}.auth.HttpBasicAuth; +import {{invokerPackage}}.auth.ApiKeyAuth; +import {{invokerPackage}}.auth.OAuth; + +import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer; +import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner; + +public class ApiClient { + + private String basePath = "{{{basePath}}}"; + private boolean debugging = false; + private Map defaultHeaderMap = new HashMap(); + private String tempFolderPath = null; + + private Map authentications; + + private DateFormat dateFormat; + private DateFormat datetimeFormat; + private boolean lenientDatetimeFormat; + private int dateLength; + + private InputStream sslCaCert; + private boolean verifyingSsl; + private KeyManager[] keyManagers; + + private OkHttpClient httpClient; + private JSON json; + + private HttpLoggingInterceptor loggingInterceptor; + + private LWAAuthorizationSigner lwaAuthorizationSigner; + private AWSSigV4Signer awsSigV4Signer; + + /* + * Constructor for ApiClient + */ + public ApiClient() { + httpClient = new OkHttpClient(); + + {{#useGzipFeature}} + // Enable gzip request compression + httpClient.interceptors().add(new GzipRequestInterceptor()); + {{/useGzipFeature}} + + verifyingSsl = true; + + json = new JSON(); + + // Set default User-Agent. + setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{artifactVersion}}}/java{{/httpUserAgent}}"); + + // Setup authentications (key: authentication name, value: authentication). + authentications = new HashMap();{{#authMethods}}{{#isBasic}} + authentications.put("{{name}}", new HttpBasicAuth());{{/isBasic}}{{#isApiKey}} + authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} + authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}} + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * Get base path + * + * @return Baes path + */ + public String getBasePath() { + return basePath; + } + + /** + * Set base path + * + * @param basePath Base path of the URL (e.g {{{basePath}}} + * @return An instance of OkHttpClient + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + + /** + * Set HTTP client + * + * @param httpClient An instance of OkHttpClient + * @return Api Client + */ + public ApiClient setHttpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + /** + * Get JSON + * + * @return JSON object + */ + public JSON getJSON() { + return json; + } + + /** + * Set JSON + * + * @param json JSON object + * @return Api client + */ + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + /** + * True if isVerifyingSsl flag is on + * + * @return True if isVerifySsl flag is on + */ + public boolean isVerifyingSsl() { + return verifyingSsl; + } + + /** + * Configure whether to verify certificate and hostname when making https requests. + * Default to true. + * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks. + * + * @param verifyingSsl True to verify TLS/SSL connection + * @return ApiClient + */ + public ApiClient setVerifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + applySslSettings(); + return this; + } + + /** + * Get SSL CA cert. + * + * @return Input stream to the SSL CA cert + */ + public InputStream getSslCaCert() { + return sslCaCert; + } + + /** + * Configure the CA certificate to be trusted when making https requests. + * Use null to reset to default. + * + * @param sslCaCert input stream for SSL CA cert + * @return ApiClient + */ + public ApiClient setSslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + applySslSettings(); + return this; + } + + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + /** + * Configure client keys to use for authorization in an SSL session. + * Use null to reset to default. + * + * @param managers The KeyManagers to use + * @return ApiClient + */ + public ApiClient setKeyManagers(KeyManager[] managers) { + this.keyManagers = managers; + applySslSettings(); + return this; + } + + public DateFormat getDateFormat() { + return dateFormat; + } + + public ApiClient setDateFormat(DateFormat dateFormat) { + this.json.setDateFormat(dateFormat); + return this; + } + + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + this.json.setSqlDateFormat(dateFormat); + return this; + } + + {{#joda}} + public ApiClient setDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; + } + + {{/joda}} + {{#jsr310}} + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; + } + + {{/jsr310}} + public ApiClient setLenientOnJson(boolean lenientOnJson) { + this.json.setLenientOnJson(lenientOnJson); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + * + * @param userAgent HTTP request's user agent + * @return ApiClient + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return ApiClient + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * + * @return True if debugging is enabled, false otherwise. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return ApiClient + */ + public ApiClient setDebugging(boolean debugging) { + if (debugging != this.debugging) { + if (debugging) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(Level.BODY); + httpClient.interceptors().add(loggingInterceptor); + } else { + httpClient.interceptors().remove(loggingInterceptor); + loggingInterceptor = null; + } + } + this.debugging = debugging; + return this; + } + + /** + * The path of temporary folder used to store downloaded files from endpoints + * with file response. The default value is null, i.e. using + * the system's default tempopary folder. + * + * @see createTempFile + * @return Temporary folder path + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + /** + * Set the temporary folder path (for downloading files) + * + * @param tempFolderPath Temporary folder path + * @return ApiClient + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getConnectTimeout() { + return httpClient.getConnectTimeout(); + } + + /** + * Sets the connect timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param connectionTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + httpClient.setConnectTimeout(connectionTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.getReadTimeout(); + } + + /** + * Sets the read timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.getWriteTimeout(); + } + + /** + * Sets the write timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Sets the LWAAuthorizationSigner + * + * @param lwaAuthorizationSigner LWAAuthorizationSigner instance + * @return Api client + */ + public ApiClient setLWAAuthorizationSigner(LWAAuthorizationSigner lwaAuthorizationSigner) { + this.lwaAuthorizationSigner = lwaAuthorizationSigner; + return this; + } + + /** + * Sets the AWSSigV4Signer + * + * @param awsSigV4Signer AWSSigV4Signer instance + * @return Api client + */ + public ApiClient setAWSSigV4Signer(AWSSigV4Signer awsSigV4Signer) { + this.awsSigV4Signer = awsSigV4Signer; + return this; + } + + /** + * Format the given parameter object into string. + * + * @param param Parameter + * @return String representation of the parameter + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date {{#joda}}|| param instanceof DateTime || param instanceof LocalDate{{/joda}}{{#jsr310}}|| param instanceof OffsetDateTime || param instanceof LocalDate{{/jsr310}}) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = json.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection)param) { + if (b.length() > 0) { + b.append(","); + } + b.append(String.valueOf(o)); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List parameterToPair(String name, Object value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) return params; + + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder() ; + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + return params; + } + + /** + * Sanitize filename by removing path. + * e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + public String sanitizeFilename(String filename) { + return filename.replaceAll(".*[/\\\\]", ""); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * "* / *" is also default to JSON + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * Select the Accept header's value from the given accepts array: + * if JSON exists in the given array, use it; + * otherwise use all of them (joining into a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, + * null will be returned (not to set the Accept header explicitly). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: + * if JSON exists in the given array, use it; + * otherwise use the first one of the array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, + * or matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * + * @param str String to be escaped + * @return Escaped string + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Deserialize response body to Java object, according to the return type and + * the Content-Type response header. + * + * @param Type + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + * @throws ApiException If fail to deserialize response body, i.e. cannot read response body + * or the Content-Type of the response is not supported. + */ + @SuppressWarnings("unchecked") + public T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) { + return null; + } + + if ("byte[]".equals(returnType.toString())) { + // Handle binary response (byte array). + try { + return (T) response.body().bytes(); + } catch (IOException e) { + throw new ApiException(e); + } + } else if (returnType.equals(File.class)) { + // Handle file downloading. + return (T) downloadFileFromResponse(response); + } + + String respBody; + try { + if (response.body() != null) + respBody = response.body().string(); + else + respBody = null; + } catch (IOException e) { + throw new ApiException(e); + } + + if (respBody == null || "".equals(respBody)) { + return null; + } + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; + } + if (isJsonMime(contentType)) { + return json.deserialize(respBody, returnType); + } else if (returnType.equals(String.class)) { + // Expecting string, return the raw response body. + return (T) respBody; + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + respBody); + } + } + + /** + * Serialize the given Java object into request body according to the object's + * class and the request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized request body + * @throws ApiException If fail to serialize the given object + */ + public RequestBody serialize(Object obj, String contentType) throws ApiException { + if (obj instanceof byte[]) { + // Binary (byte array) body parameter support. + return RequestBody.create(MediaType.parse(contentType), (byte[]) obj); + } else if (obj instanceof File) { + // File body parameter support. + return RequestBody.create(MediaType.parse(contentType), (File) obj); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = json.serialize(obj); + } else { + content = null; + } + return RequestBody.create(MediaType.parse(contentType), content); + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * Download file from the given response. + * + * @param response An instance of the Response object + * @throws ApiException If fail to read file content from response and write to disk + * @return Downloaded file + */ + public File downloadFileFromResponse(Response response) throws ApiException { + try { + File file = prepareDownloadFile(response); + BufferedSink sink = Okio.buffer(Okio.sink(file)); + sink.writeAll(response.body().source()); + sink.close(); + return file; + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Prepare file for download + * + * @param response An instance of the Response object + * @throws IOException If fail to prepare file for download + * @return Prepared file for the download + */ + public File prepareDownloadFile(Response response) throws IOException { + String filename = null; + String contentDisposition = response.header("Content-Disposition"); + if (contentDisposition != null && !"".equals(contentDisposition)) { + // Get filename from the Content-Disposition header. + Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + Matcher matcher = pattern.matcher(contentDisposition); + if (matcher.find()) { + filename = sanitizeFilename(matcher.group(1)); + } + } + + String prefix = null; + String suffix = null; + if (filename == null) { + prefix = "download-"; + suffix = ""; + } else { + int pos = filename.lastIndexOf("."); + if (pos == -1) { + prefix = filename + "-"; + } else { + prefix = filename.substring(0, pos) + "-"; + suffix = filename.substring(pos); + } + // File.createTempFile requires the prefix to be at least three characters long + if (prefix.length() < 3) + prefix = "download-"; + } + + if (tempFolderPath == null) + return File.createTempFile(prefix, suffix); + else + return File.createTempFile(prefix, suffix, new File(tempFolderPath)); + } + + /** + * {@link #execute(Call, Type)} + * + * @param Type + * @param call An instance of the Call object + * @throws ApiException If fail to execute the call + * @return ApiResponse<T> + */ + public ApiResponse execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and + * data, which is a Java object deserialized from response body and would be null + * when returnType is null. + * @throws ApiException If fail to execute the call + */ + public ApiResponse execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + public void executeAsync(Call call, ApiCallback callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @see #execute(Call, Type) + * @param Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + */ + @SuppressWarnings("unchecked") + public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Request request, IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + + @Override + public void onResponse(Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess(result, response.code(), response.headers().toMultimap()); + } + }); + } + + /** + * Handle the given response, return the deserialized object when the response is successful. + * + * @param Type + * @param response Response + * @param returnType Return type + * @throws ApiException If the response has a unsuccessful status code or + * fail to deserialize the response body + * @return Type + */ + public T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + if (response.body() != null) { + try { + response.body().close(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param progressRequestListener Progress request listener + * @return The HTTP call + * @throws ApiException If fail to serialize the request body object + */ + public Call buildCall(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener); + + return httpClient.newCall(request); + } + + /** + * Build an HTTP request with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param progressRequestListener Progress request listener + * @return The HTTP request + * @throws ApiException If fail to serialize the request body object + */ + public Request buildRequest(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + updateParamsForAuth(authNames, queryParams, headerParams); + + final String url = buildUrl(path, queryParams, collectionQueryParams); + final Request.Builder reqBuilder = new Request.Builder().url(url); + processHeaderParams(headerParams, reqBuilder); + + String contentType = (String) headerParams.get("Content-Type"); + // ensuring a default content type + if (contentType == null) { + contentType = "application/json"; + } + + RequestBody reqBody; + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create(MediaType.parse(contentType), ""); + } + } else { + reqBody = serialize(body, contentType); + } + + Request request = null; + + if(progressRequestListener != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, progressRequestListener); + request = reqBuilder.method(method, progressRequestBody).build(); + } else { + request = reqBuilder.method(method, reqBody).build(); + } + + request = lwaAuthorizationSigner.sign(request); + request = awsSigV4Signer.sign(request); + + return request; + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + public String buildUrl(String path, List queryParams, List collectionQueryParams) { + final StringBuilder url = new StringBuilder(); + url.append(basePath).append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + return url.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + * + * @param headerParams Header parameters in the ofrm of Map + * @param reqBuilder Reqeust.Builder + */ + public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { + for (Entry param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + */ + public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + auth.applyToParams(queryParams, headerParams); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyFormEncoding(Map formParams) { + FormEncodingBuilder formBuilder = new FormEncodingBuilder(); + for (Entry param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, + * which could contain text fields and file fields. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyMultipart(Map formParams) { + MultipartBuilder mpBuilder = new MultipartBuilder().type(MultipartBuilder.FORM); + for (Entry param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); + } else { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); + mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + /** + * Apply SSL related settings to httpClient according to the current values of + * verifyingSsl and sslCaCert. + */ + private void applySslSettings() { + try { + TrustManager[] trustManagers = null; + HostnameVerifier hostnameVerifier = null; + if (!verifyingSsl) { + TrustManager trustAll = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} + @Override + public X509Certificate[] getAcceptedIssuers() { return null; } + }; + SSLContext sslContext = SSLContext.getInstance("TLS"); + trustManagers = new TrustManager[]{ trustAll }; + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { return true; } + }; + } else if (sslCaCert != null) { + char[] password = null; // Any password will work. + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + Integer.toString(index++); + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(caKeyStore); + trustManagers = trustManagerFactory.getTrustManagers(); + } + + if (keyManagers != null || trustManagers != null) { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + httpClient.setSslSocketFactory(sslContext.getSocketFactory()); + } else { + httpClient.setSslSocketFactory(null); + } + httpClient.setHostnameVerifier(hostnameVerifier); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/StringUtil.mustache b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/StringUtil.mustache new file mode 100644 index 0000000..a9343a9 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/StringUtil.mustache @@ -0,0 +1,54 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +{{>generatedAnnotation}} +public class StringUtil { + /** + * Check if the given array contains the given value (with case-insensitive comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) return true; + if (value != null && value.equalsIgnoreCase(str)) return true; + } + return false; + } + + /** + * Join an array of strings with the given separator. + *

+ * Note: This might be replaced by utility method from commons-lang or guava someday + * if one of those libraries is added as dependency. + *

+ * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) return ""; + + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); + } + + /** + * Check if the given value is null or an empty string + * + * @param value The value to check + * @return true if the value is null or empty + */ + public static boolean isEmpty(String value) { + return value == null || value.isEmpty(); + } +} diff --git a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache new file mode 100644 index 0000000..6d13e68 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache @@ -0,0 +1,317 @@ +{{>licenseInfo}} + +package {{package}}; + +import {{invokerPackage}}.ApiCallback; +import {{invokerPackage}}.ApiClient; +import {{invokerPackage}}.ApiException; +import {{invokerPackage}}.ApiResponse; +import {{invokerPackage}}.Configuration; +import {{invokerPackage}}.Pair; +import {{invokerPackage}}.ProgressRequestBody; +import {{invokerPackage}}.ProgressResponseBody; +import {{invokerPackage}}.StringUtil; +{{#performBeanValidation}} +import {{invokerPackage}}.BeanValidationException; +{{/performBeanValidation}} + +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; + +{{#useBeanValidation}} +import javax.validation.constraints.*; +{{/useBeanValidation}} +{{#performBeanValidation}} +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.executable.ExecutableValidator; +import java.util.Set; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +{{/performBeanValidation}} + +{{#imports}}import {{import}}; +{{/imports}} + +import java.lang.reflect.Type; +{{^fullJavaUtil}} +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +{{/fullJavaUtil}} + +import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer; +import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner; +import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials; +import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentials; + +{{#operations}} +public class {{classname}} { + private ApiClient {{localVariablePrefix}}apiClient; + + {{classname}}() { + this(Configuration.getDefaultApiClient()); + } + + public {{classname}}(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + public ApiClient getApiClient() { + return {{localVariablePrefix}}apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + {{#operation}} + /** + * Build call for {{operationId}}{{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} + * @param progressListener Progress listener + * @param progressRequestListener Progress request listener + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public com.squareup.okhttp.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + + // create path and map variables + String {{localVariablePrefix}}localVarPath = "{{{path}}}"{{#pathParams}} + .replaceAll("\\{" + "{{baseName}}" + "\\}", {{localVariablePrefix}}apiClient.escapeString({{{paramName}}}.toString())){{/pathParams}}; + + {{javaUtilPrefix}}List {{localVariablePrefix}}localVarQueryParams = new {{javaUtilPrefix}}ArrayList(); + {{javaUtilPrefix}}List {{localVariablePrefix}}localVarCollectionQueryParams = new {{javaUtilPrefix}}ArrayList();{{#queryParams}} + if ({{paramName}} != null) + {{localVariablePrefix}}{{#collectionFormat}}localVarCollectionQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}}));{{/queryParams}} + + {{javaUtilPrefix}}Map {{localVariablePrefix}}localVarHeaderParams = new {{javaUtilPrefix}}HashMap();{{#headerParams}} + if ({{paramName}} != null) + {{localVariablePrefix}}localVarHeaderParams.put("{{baseName}}", {{localVariablePrefix}}apiClient.parameterToString({{paramName}}));{{/headerParams}} + + {{javaUtilPrefix}}Map {{localVariablePrefix}}localVarFormParams = new {{javaUtilPrefix}}HashMap();{{#formParams}} + if ({{paramName}} != null) + {{localVariablePrefix}}localVarFormParams.put("{{baseName}}", {{paramName}});{{/formParams}} + + final String[] {{localVariablePrefix}}localVarAccepts = { + {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} + }; + final String {{localVariablePrefix}}localVarAccept = {{localVariablePrefix}}apiClient.selectHeaderAccept({{localVariablePrefix}}localVarAccepts); + if ({{localVariablePrefix}}localVarAccept != null) {{localVariablePrefix}}localVarHeaderParams.put("Accept", {{localVariablePrefix}}localVarAccept); + + final String[] {{localVariablePrefix}}localVarContentTypes = { + {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} + }; + final String {{localVariablePrefix}}localVarContentType = {{localVariablePrefix}}apiClient.selectHeaderContentType({{localVariablePrefix}}localVarContentTypes); + {{localVariablePrefix}}localVarHeaderParams.put("Content-Type", {{localVariablePrefix}}localVarContentType); + + if(progressListener != null) { + apiClient.getHttpClient().networkInterceptors().add(new com.squareup.okhttp.Interceptor() { + @Override + public com.squareup.okhttp.Response intercept(com.squareup.okhttp.Interceptor.Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), progressListener)) + .build(); + } + }); + } + + String[] {{localVariablePrefix}}localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; + return {{localVariablePrefix}}apiClient.buildCall({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarCollectionQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAuthNames, progressRequestListener); + } + + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + @SuppressWarnings("rawtypes") + private com.squareup.okhttp.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + {{^performBeanValidation}} + {{#allParams}}{{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + throw new ApiException("Missing the required parameter '{{paramName}}' when calling {{operationId}}(Async)"); + } + {{/required}}{{/allParams}} + + com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}progressListener, progressRequestListener); + return {{localVariablePrefix}}call; + + {{/performBeanValidation}} + {{#performBeanValidation}} + try { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + ExecutableValidator executableValidator = factory.getValidator().forExecutables(); + + Object[] parameterValues = { {{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}} }; + Method method = this.getClass().getMethod("{{operationId}}WithHttpInfo"{{#allParams}}, {{#isListContainer}}java.util.List{{/isListContainer}}{{#isMapContainer}}java.util.Map{{/isMapContainer}}{{^isListContainer}}{{^isMapContainer}}{{{dataType}}}{{/isMapContainer}}{{/isListContainer}}.class{{/allParams}}); + Set> violations = executableValidator.validateParameters(this, method, + parameterValues); + + if (violations.size() == 0) { + com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}progressListener, progressRequestListener); + return {{localVariablePrefix}}call; + + } else { + throw new BeanValidationException((Set) violations); + } + } catch (NoSuchMethodException e) { + e.printStackTrace(); + throw new ApiException(e.getMessage()); + } catch (SecurityException e) { + e.printStackTrace(); + throw new ApiException(e.getMessage()); + } + + {{/performBeanValidation}} + } + + /** + * {{summary}} + * {{notes}}{{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#returnType}} + * @return {{returnType}}{{/returnType}} + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { + {{#returnType}}ApiResponse<{{{returnType}}}> {{localVariablePrefix}}resp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + return {{localVariablePrefix}}resp.getData();{{/returnType}} + } + + /** + * {{summary}} + * {{notes}}{{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} + * @return ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Void{{/returnType}}> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { + com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null, null); + {{#returnType}}Type {{localVariablePrefix}}localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType(); + return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call, {{localVariablePrefix}}localVarReturnType);{{/returnType}}{{^returnType}}return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call);{{/returnType}} + } + + /** + * {{summary}} (asynchronously) + * {{notes}}{{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} + * @param callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public com.squareup.okhttp.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException { + + ProgressResponseBody.ProgressListener progressListener = null; + ProgressRequestBody.ProgressRequestListener progressRequestListener = null; + + if (callback != null) { + progressListener = new ProgressResponseBody.ProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + callback.onDownloadProgress(bytesRead, contentLength, done); + } + }; + + progressRequestListener = new ProgressRequestBody.ProgressRequestListener() { + @Override + public void onRequestProgress(long bytesWritten, long contentLength, boolean done) { + callback.onUploadProgress(bytesWritten, contentLength, done); + } + }; + } + + com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}progressListener, progressRequestListener); + {{#returnType}}Type {{localVariablePrefix}}localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType(); + {{localVariablePrefix}}apiClient.executeAsync({{localVariablePrefix}}call, {{localVariablePrefix}}localVarReturnType, {{localVariablePrefix}}callback);{{/returnType}}{{^returnType}}{{localVariablePrefix}}apiClient.executeAsync({{localVariablePrefix}}call, {{localVariablePrefix}}callback);{{/returnType}} + return {{localVariablePrefix}}call; + } + {{/operation}} + + public static class Builder { + private AWSAuthenticationCredentials awsAuthenticationCredentials; + private LWAAuthorizationCredentials lwaAuthorizationCredentials; + private String endpoint; + + public Builder awsAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) { + this.awsAuthenticationCredentials = awsAuthenticationCredentials; + return this; + } + + public Builder lwaAuthorizationCredentials(LWAAuthorizationCredentials lwaAuthorizationCredentials) { + this.lwaAuthorizationCredentials = lwaAuthorizationCredentials; + return this; + } + + public Builder endpoint(String endpoint) { + this.endpoint = endpoint; + return this; + } + + public {{classname}} build() { + if (awsAuthenticationCredentials == null) { + throw new RuntimeException("AWSAuthenticationCredentials not set"); + } + + if (lwaAuthorizationCredentials == null) { + throw new RuntimeException("LWAAuthorizationCredentials not set"); + } + + if (StringUtil.isEmpty(endpoint)) { + throw new RuntimeException("Endpoint not set"); + } + + AWSSigV4Signer awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials); + LWAAuthorizationSigner lwaAuthorizationSigner = new LWAAuthorizationSigner(lwaAuthorizationCredentials); + + return new {{classname}}(new ApiClient() + .setAWSSigV4Signer(awsSigV4Signer) + .setLWAAuthorizationSigner(lwaAuthorizationSigner) + .setBasePath(endpoint)); + } + } +} +{{/operations}} \ No newline at end of file diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java new file mode 100644 index 0000000..6a7026f --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java @@ -0,0 +1,30 @@ +package com.amazon.SellingPartnerAPIAA; + +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; + +/** + * AWSAuthenticationCredentials + */ +@Data +@Builder +public class AWSAuthenticationCredentials { + /** + * AWS IAM User Access Key Id + */ + @NonNull + private String accessKeyId; + + /** + * AWS IAM User Secret Key + */ + @NonNull + private String secretKey; + + /** + * AWS Region + */ + @NonNull + private String region; +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java new file mode 100644 index 0000000..ba4e9f0 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java @@ -0,0 +1,48 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.amazonaws.SignableRequest; +import com.amazonaws.auth.AWS4Signer; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.BasicAWSCredentials; +import com.squareup.okhttp.Request; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +/** + * AWS Signature Version 4 Signer + */ +public class AWSSigV4Signer { + private static final String SERVICE_NAME = "execute-api"; + + @Setter(AccessLevel.PACKAGE) + @Getter(AccessLevel.PACKAGE) + private AWS4Signer aws4Signer; + + private AWSCredentials awsCredentials; + + /** + * + * @param awsAuthenticationCredentials AWS Developer Account Credentials + */ + public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials) { + aws4Signer = new AWS4Signer(); + aws4Signer.setServiceName(SERVICE_NAME); + aws4Signer.setRegionName(awsAuthenticationCredentials.getRegion()); + awsCredentials = new BasicAWSCredentials(awsAuthenticationCredentials.getAccessKeyId(), + awsAuthenticationCredentials.getSecretKey()); + } + + /** + * Signs a Request with AWS Signature Version 4 + * + * @param originalRequest Request to sign (treated as immutable) + * @return Copy of originalRequest with AWS Signature + */ + public Request sign(Request originalRequest) { + SignableRequest signableRequest = new SignableRequestImpl(originalRequest); + aws4Signer.sign(signableRequest, awsCredentials); + + return (Request) signableRequest.getOriginalRequestObject(); + } +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAccessTokenRequestMeta.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAccessTokenRequestMeta.java new file mode 100644 index 0000000..2313397 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAccessTokenRequestMeta.java @@ -0,0 +1,25 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +class LWAAccessTokenRequestMeta { + @SerializedName("grant_type") + private String grantType; + + @SerializedName("refresh_token") + private String refreshToken; + + @SerializedName("client_id") + private String clientId; + + @SerializedName("client_secret") + private String clientSecret; + + @SerializedName("scope") + private LWAClientScopes scopes; + +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationCredentials.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationCredentials.java new file mode 100644 index 0000000..c9dee81 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationCredentials.java @@ -0,0 +1,65 @@ +package com.amazon.SellingPartnerAPIAA; + +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; + +import java.util.Arrays; +import java.util.HashSet; + +/** + * LWAAuthorizationCredentials + */ +@Data +@Builder +public class LWAAuthorizationCredentials { + /** + * LWA Client Id + */ + @NonNull + private String clientId; + + /** + * LWA Client Secret + */ + @NonNull + private String clientSecret; + + /** + * LWA Refresh Token + */ + private String refreshToken; + + /** + * LWA Authorization Server Endpoint + */ + @NonNull + private String endpoint; + + /** + * LWA Client Scopes + */ + private LWAClientScopes scopes; + + public static class LWAAuthorizationCredentialsBuilder { + + { + scopes = new LWAClientScopes(new HashSet<>()); + } + + public LWAAuthorizationCredentialsBuilder withScope(String scope) { + return withScopes(scope); + } + + public LWAAuthorizationCredentialsBuilder withScopes(String... scopes) { + if (scopes != null) { + Arrays.stream(scopes) + .forEach(this.scopes::addScope); + } + return this; + } + + + } + +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java new file mode 100644 index 0000000..f429983 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java @@ -0,0 +1,55 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.squareup.okhttp.Request; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +/** + * LWA Authorization Signer + */ +public class LWAAuthorizationSigner { + private static final String SIGNED_ACCESS_TOKEN_HEADER_NAME = "x-amz-access-token"; + private final String tokenRequestGrantType; + + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private LWAClient lwaClient; + + private LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta; + + /** + * + * @param lwaAuthorizationCredentials LWA Authorization Credentials for token exchange + */ + public LWAAuthorizationSigner(LWAAuthorizationCredentials lwaAuthorizationCredentials) { + + lwaClient = new LWAClient(lwaAuthorizationCredentials.getEndpoint()); + + if (!lwaAuthorizationCredentials.getScopes().isEmpty()) { + tokenRequestGrantType = "client_credentials"; + } else { + tokenRequestGrantType = "refresh_token"; + } + + lwaAccessTokenRequestMeta = LWAAccessTokenRequestMeta.builder() + .clientId(lwaAuthorizationCredentials.getClientId()) + .clientSecret(lwaAuthorizationCredentials.getClientSecret()) + .refreshToken(lwaAuthorizationCredentials.getRefreshToken()) + .grantType(tokenRequestGrantType).scopes(lwaAuthorizationCredentials.getScopes()) + .build(); + } + + /** + * Signs a Request with an LWA Access Token + * @param originalRequest Request to sign (treated as immutable) + * @return Copy of originalRequest with LWA signature + */ + public Request sign(Request originalRequest) { + String accessToken = lwaClient.getAccessToken(lwaAccessTokenRequestMeta); + + return originalRequest.newBuilder() + .addHeader(SIGNED_ACCESS_TOKEN_HEADER_NAME, accessToken) + .build(); + } +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java new file mode 100644 index 0000000..5068400 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java @@ -0,0 +1,58 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import java.io.IOException; + +class LWAClient { + private static final String ACCESS_TOKEN_KEY = "access_token"; + private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); + + @Getter + private String endpoint; + + @Setter(AccessLevel.PACKAGE) + private OkHttpClient okHttpClient; + + LWAClient(String endpoint) { + okHttpClient = new OkHttpClient(); + this.endpoint = endpoint; + } + + String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { + RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, + new Gson().toJson(lwaAccessTokenRequestMeta)); + Request accessTokenRequest = new Request.Builder() + .url(endpoint) + .post(requestBody) + .build(); + + String accessToken; + try { + Response response = okHttpClient.newCall(accessTokenRequest).execute(); + if (!response.isSuccessful()) { + throw new IOException("Unsuccessful LWA token exchange"); + } + + JsonObject responseJson = new JsonParser() + .parse(response.body().string()) + .getAsJsonObject(); + + accessToken = responseJson.get(ACCESS_TOKEN_KEY).getAsString(); + } catch (Exception e) { + throw new RuntimeException("Error getting LWA Access Token", e); + } + + return accessToken; + } +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopes.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopes.java new file mode 100644 index 0000000..b2de370 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopes.java @@ -0,0 +1,27 @@ +package com.amazon.SellingPartnerAPIAA; + + +import com.google.gson.annotations.JsonAdapter; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Set; + +@AllArgsConstructor +@Getter +@JsonAdapter(LWAClientScopesSerializerDeserializer.class) +public class LWAClientScopes { + + private final Set scopes; + + protected void addScope(String scope) { + scopes.add(scope); + + } + + protected boolean isEmpty() { + return scopes.isEmpty(); + } + + +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializer.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializer.java new file mode 100644 index 0000000..b821658 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializer.java @@ -0,0 +1,34 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class LWAClientScopesSerializerDeserializer implements JsonDeserializer, + JsonSerializer { + + @Override + public JsonElement serialize(LWAClientScopes src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(String.join(" ", src.getScopes())); + } + + @Override + public LWAClientScopes deserialize(JsonElement jsonElement, Type type, + JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { + JsonObject jsonObj = jsonElement.getAsJsonObject(); + Set scopeSet = new HashSet<>(Arrays.asList(jsonObj.get("scope").getAsString().split(" "))); + return new LWAClientScopes(scopeSet); + + } +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/ScopeConstants.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/ScopeConstants.java new file mode 100644 index 0000000..8d71dd1 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/ScopeConstants.java @@ -0,0 +1,11 @@ +package com.amazon.SellingPartnerAPIAA; + +public final class ScopeConstants { + + private ScopeConstants(){ + } + + public static final String SCOPE_NOTIFICATIONS_API = "sellingpartnerapi::notifications"; + public static final String SCOPE_MIGRATION_API = "sellingpartnerapi::migration"; + +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java new file mode 100644 index 0000000..9e0d196 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java @@ -0,0 +1,153 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.amazonaws.ReadLimitInfo; +import com.amazonaws.SignableRequest; +import com.amazonaws.http.HttpMethodName; +import com.squareup.okhttp.HttpUrl; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.Request; +import okio.Buffer; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class SignableRequestImpl implements SignableRequest { + private static final String CONTENT_TYPE_HEADER_NAME = "Content-Type"; + private Request originalRequest; + private Request.Builder signableRequestBuilder; + + SignableRequestImpl(Request originalRequest) { + this.originalRequest = originalRequest; + signableRequestBuilder = originalRequest.newBuilder(); + } + + @Override + public void addHeader(String name, String value) { + signableRequestBuilder.addHeader(name, value); + } + + @Override + public void addParameter(String name, String value) { + HttpUrl newUrl = signableRequestBuilder.build() + .httpUrl() + .newBuilder() + .addEncodedQueryParameter(name, value) + .build(); + + signableRequestBuilder.url(newUrl); + } + + @Override + public void setContent(InputStream inputStream) { + throw new UnsupportedOperationException(); + } + + @Override + public Map getHeaders() { + Map headers = new HashMap<>(); + + Request requestSnapshot = signableRequestBuilder.build(); + requestSnapshot.headers() + .names() + .forEach(headerName -> headers.put(headerName, requestSnapshot.header(headerName))); + + if (requestSnapshot.body() != null) { + MediaType contentType = requestSnapshot.body().contentType(); + if (contentType != null) { + headers.put(CONTENT_TYPE_HEADER_NAME, contentType.toString()); + } + } + + return headers; + } + + @Override + public String getResourcePath() { + try { + return originalRequest.url() + .toURI() + .getPath(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public Map> getParameters() { + Map> parameters = new HashMap<>(); + try { + List nameValuePairs = URLEncodedUtils.parse(originalRequest.url().toURI(), + StandardCharsets.UTF_8); + nameValuePairs.forEach(nameValuePair -> parameters.put(nameValuePair.getName(), + Collections.singletonList(nameValuePair.getValue()))); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + return parameters; + } + + @Override + public URI getEndpoint() { + URI uri = null; + try { + uri = originalRequest.url().toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + return URI.create(String.format("%s://%s", uri.getScheme(), uri.getHost())); + } + + @Override + public HttpMethodName getHttpMethod() { + return HttpMethodName.fromValue(originalRequest.method().toUpperCase()); + } + + @Override + public int getTimeOffset() { + return 0; + } + + @Override + public InputStream getContent() { + ByteArrayInputStream inputStream = null; + + if (originalRequest.body() != null) { + try { + Buffer buffer = new Buffer(); + originalRequest.body().writeTo(buffer); + inputStream = new ByteArrayInputStream(buffer.readByteArray()); + } catch (IOException e) { + throw new RuntimeException("Unable to buffer request body", e); + } + } + + return inputStream; + } + + @Override + public InputStream getContentUnwrapped() { + return getContent(); + } + + @Override + public ReadLimitInfo getReadLimitInfo() { + return null; + } + + @Override + public Object getOriginalRequestObject() { + return signableRequestBuilder.build(); + } +} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java new file mode 100644 index 0000000..3cd9c6a --- /dev/null +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java @@ -0,0 +1,83 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.amazonaws.SignableRequest; +import com.amazonaws.auth.AWS4Signer; +import com.amazonaws.auth.AWSCredentials; +import com.squareup.okhttp.Request; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class AWSSigV4SignerTest { + private static final String TEST_ACCESS_KEY_ID = "aKey"; + private static final String TEST_SECRET_KEY = "sKey"; + private static final String TEST_REGION = "us-east"; + + @Mock + private AWS4Signer mockAWS4Signer; + + private AWSSigV4Signer underTest; + + @Before + public void init() { + underTest = new AWSSigV4Signer(AWSAuthenticationCredentials.builder() + .accessKeyId(TEST_ACCESS_KEY_ID) + .secretKey(TEST_SECRET_KEY) + .region(TEST_REGION) + .build() + ); + } + + @Test + public void signRequestUsingProvidedCredentials() { + ArgumentCaptor awsCredentialsArgumentCaptor = ArgumentCaptor.forClass(AWSCredentials.class); + underTest.setAws4Signer(mockAWS4Signer); + + doNothing() + .when(mockAWS4Signer) + .sign(any(SignableRequest.class), awsCredentialsArgumentCaptor.capture()); + + underTest.sign(new Request.Builder().url("https://api.amazon.com").build()); + + AWSCredentials actualAWSCredentials = awsCredentialsArgumentCaptor.getValue(); + + assertEquals(TEST_ACCESS_KEY_ID, actualAWSCredentials.getAWSAccessKeyId()); + assertEquals(TEST_SECRET_KEY, actualAWSCredentials.getAWSSecretKey()); + } + + @Test + public void setSignerRegion() { + assertEquals(TEST_REGION, underTest.getAws4Signer().getRegionName()); + } + + @Test + public void setSignerServiceName() { + assertEquals("execute-api", underTest.getAws4Signer().getServiceName()); + } + + @Test + public void returnSignedRequest() { + ArgumentCaptor signableRequestArgumentCaptor = ArgumentCaptor.forClass(SignableRequest.class); + underTest.setAws4Signer(mockAWS4Signer); + + Request actualSignedRequest = underTest.sign(new Request.Builder() + .url("http://api.amazon.com") + .build()); + + verify(mockAWS4Signer) + .sign(signableRequestArgumentCaptor.capture(), any(AWSCredentials.class)); + + SignableRequest actualSignableRequest = signableRequestArgumentCaptor.getValue(); + + assertEquals(((Request)actualSignableRequest.getOriginalRequestObject()).url(), actualSignedRequest.url()); + } +} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java new file mode 100644 index 0000000..10b5982 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java @@ -0,0 +1,131 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.squareup.okhttp.Request; +import org.junit.Assert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.stream.Stream; + +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API; +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LWAAuthorizationSignerTest { + private static final String TEST_REFRESH_TOKEN = "rToken"; + private static final String TEST_CLIENT_SECRET = "cSecret"; + private static final String TEST_CLIENT_ID = "cId"; + private static final String TEST_ENDPOINT = "https://www.amazon.com/lwa"; + private static final String TEST_SCOPE_1 = SCOPE_NOTIFICATIONS_API; + private static final String TEST_SCOPE_2 = SCOPE_MIGRATION_API; + private static final String SELLER_TYPE_SELLER = "seller"; + private static final String SELLER_TYPE_SELLERLESS = "sellerless"; + + + private Request request; + private static LWAAuthorizationSigner underTestSeller; + private static LWAAuthorizationSigner underTestSellerless; + + static { + + underTestSeller = new LWAAuthorizationSigner(LWAAuthorizationCredentials.builder() + .clientId(TEST_CLIENT_ID) + .clientSecret(TEST_CLIENT_SECRET) + .refreshToken(TEST_REFRESH_TOKEN) + .endpoint(TEST_ENDPOINT) + .build()); + + underTestSellerless = new LWAAuthorizationSigner(LWAAuthorizationCredentials.builder() + .clientId(TEST_CLIENT_ID) + .clientSecret(TEST_CLIENT_SECRET) + .withScopes(TEST_SCOPE_1, TEST_SCOPE_2) + .endpoint(TEST_ENDPOINT) + .build()); + } + + @BeforeEach + public void init() { + request = new Request.Builder() + .url("https://www.amazon.com/api") + .build(); + + } + + public static Stream lwaAuthSigner(){ + + return Stream.of( + Arguments.of(SELLER_TYPE_SELLER, underTestSeller), + Arguments.of(SELLER_TYPE_SELLERLESS, underTestSellerless) + ); + } + + @ParameterizedTest + @MethodSource("lwaAuthSigner") + public void initializeLWAClientWithConfiguredEndpoint(String sellerType, LWAAuthorizationSigner testAuthSigner) { + assertEquals(TEST_ENDPOINT, testAuthSigner.getLwaClient().getEndpoint()); + } + + @ParameterizedTest + @MethodSource("lwaAuthSigner") + public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) { + LWAClient mockLWAClient = mock(LWAClient.class); + ArgumentCaptor lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass( + LWAAccessTokenRequestMeta.class); + + when(mockLWAClient.getAccessToken(lwaAccessTokenRequestMetaArgumentCaptor.capture())) + .thenReturn("foo"); + + testAuthSigner.setLwaClient(mockLWAClient); + testAuthSigner.sign(request); + + LWAAccessTokenRequestMeta actualLWAAccessTokenRequestMeta = lwaAccessTokenRequestMetaArgumentCaptor.getValue(); + assertEquals(TEST_REFRESH_TOKEN, actualLWAAccessTokenRequestMeta.getRefreshToken()); + assertEquals(TEST_CLIENT_SECRET, actualLWAAccessTokenRequestMeta.getClientSecret()); + assertEquals(TEST_CLIENT_ID, actualLWAAccessTokenRequestMeta.getClientId()); + + if(sellerType.equals(SELLER_TYPE_SELLER)){ + Assert.assertTrue(actualLWAAccessTokenRequestMeta.getScopes().getScopes().isEmpty()); + assertEquals("refresh_token", actualLWAAccessTokenRequestMeta.getGrantType()); + } + else if (sellerType.equals(SELLER_TYPE_SELLERLESS)){ + assertEquals(new HashSet(Arrays.asList(TEST_SCOPE_1, TEST_SCOPE_2)), actualLWAAccessTokenRequestMeta.getScopes().getScopes()); + assertEquals("client_credentials", actualLWAAccessTokenRequestMeta.getGrantType()); + } + + } + + @ParameterizedTest + @MethodSource("lwaAuthSigner") + public void returnSignedRequestWithAccessTokenFromLWAClient(String sellerType, LWAAuthorizationSigner testAuthSigner) { + LWAClient mockLWAClient = mock(LWAClient.class); + + when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class))) + .thenReturn("Azta|Foo"); + + testAuthSigner.setLwaClient(mockLWAClient); + Request actualSignedRequest = testAuthSigner.sign(request); + + assertEquals("Azta|Foo", actualSignedRequest.header("x-amz-access-token")); + } + + @ParameterizedTest + @MethodSource("lwaAuthSigner") + public void originalRequestIsImmutable(String sellerType, LWAAuthorizationSigner testAuthSigner) { + LWAClient mockLWAClient = mock(LWAClient.class); + + when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class))) + .thenReturn("Azta|Foo"); + + testAuthSigner.setLwaClient(mockLWAClient); + assertNotSame(request, testAuthSigner.sign(request)); + } +} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializerTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializerTest.java new file mode 100644 index 0000000..089f72e --- /dev/null +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientScopesSerializerDeserializerTest.java @@ -0,0 +1,78 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.google.gson.Gson; +import org.junit.Assert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API; +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API; + +public class LWAClientScopesSerializerDeserializerTest { + private static final String TEST_SCOPE_1 = SCOPE_NOTIFICATIONS_API; + private static final String TEST_SCOPE_2 = SCOPE_MIGRATION_API; + + private static final Set scopesTestSellerless = new HashSet(Arrays.asList(TEST_SCOPE_1, + TEST_SCOPE_2)); + + private static final String SELLER_TYPE_SELLER = "seller"; + private static final String SELLER_TYPE_SELLERLESS = "sellerless"; + + private Gson gson; + + @BeforeEach + public void setup() { + gson = new Gson(); + } + + public static Stream scopeSerialization(){ + + return Stream.of( + Arguments.of(SELLER_TYPE_SELLER, null), + Arguments.of(SELLER_TYPE_SELLERLESS, new LWAClientScopes(scopesTestSellerless)) + ); + } + + public static Stream scopeDeserialization(){ + + return Stream.of( + Arguments.of(SELLER_TYPE_SELLER, null), + Arguments.of(SELLER_TYPE_SELLERLESS, "{\"scope\":\"sellingpartnerapi::migration sellingpartnerapi::notifications\"}") + ); + } + + @ParameterizedTest + @MethodSource("scopeSerialization") + public void testSerializeScope(String sellerType, LWAClientScopes testScope){ + + String scopeJSON = gson.toJson(testScope); + + if (sellerType.equals(SELLER_TYPE_SELLER)) { + Assert.assertEquals("null", scopeJSON); + } + else if (sellerType.equals(SELLER_TYPE_SELLERLESS)){ + Assert.assertTrue(!scopeJSON.isEmpty()); + } + } + + @ParameterizedTest + @MethodSource("scopeDeserialization") + public void testDeserializeScope(String sellerType, String serializedValue){ + + LWAClientScopes deserializedValue = gson.fromJson(serializedValue, LWAClientScopes.class); + if (sellerType.equals(SELLER_TYPE_SELLER)) { + Assert.assertNull(deserializedValue); + } + else if (sellerType.equals(SELLER_TYPE_SELLERLESS)){ + Assert.assertEquals(deserializedValue.getScopes(),scopesTestSellerless); + } + } + +} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java new file mode 100644 index 0000000..4606f14 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java @@ -0,0 +1,185 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.squareup.okhttp.Call; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Protocol; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.ResponseBody; +import okio.Buffer; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API; +import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LWAClientTest { + private static final String TEST_ENDPOINT = "https://www.amazon.com/api"; + private static final MediaType EXPECTED_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); + private static LWAAccessTokenRequestMeta lwaAccessTokenRequestMetaSeller; + private static LWAAccessTokenRequestMeta lwaAccessTokenRequestMetaSellerless; + + private static final String TEST_SCOPE_1 = SCOPE_NOTIFICATIONS_API; + private static final String TEST_SCOPE_2 = SCOPE_MIGRATION_API; + + private static final Set scopesTestSellerless = new HashSet(Arrays.asList(TEST_SCOPE_1, + TEST_SCOPE_2)); + + private static final String SELLER_TYPE_SELLER = "seller"; + private static final String SELLER_TYPE_SELLERLESS = "sellerless"; + + @Mock + private OkHttpClient mockOkHttpClient; + + @Mock + private Call mockCall; + + private LWAClient underTest; + + static { + lwaAccessTokenRequestMetaSeller = LWAAccessTokenRequestMeta.builder() + .refreshToken("rToken") + .clientId("cId") + .clientSecret("cSecret") + .grantType("rToken") + .build(); + + lwaAccessTokenRequestMetaSellerless = LWAAccessTokenRequestMeta.builder() + .clientId("cId") + .clientSecret("cSecret") + .grantType("cCredentials") + .scopes(new LWAClientScopes(scopesTestSellerless)) + .build(); + } + + @Before + @BeforeEach + public void init() { + MockitoAnnotations.initMocks(this); + + underTest = new LWAClient(TEST_ENDPOINT); + underTest.setOkHttpClient(mockOkHttpClient); + + } + + public static Stream lwaClient(){ + + return Stream.of( + Arguments.of(SELLER_TYPE_SELLER, lwaAccessTokenRequestMetaSeller), + Arguments.of(SELLER_TYPE_SELLERLESS, lwaAccessTokenRequestMetaSellerless) + ); + } + + @Test + public void initializeEndpoint() { + assertEquals(TEST_ENDPOINT, underTest.getEndpoint()); + } + + @ParameterizedTest + @MethodSource("lwaClient") + public void makeRequestFromMeta (String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(Request.class); + when(mockOkHttpClient.newCall(requestArgumentCaptor.capture())) + .thenReturn(mockCall); + when(mockCall.execute()) + .thenReturn(buildResponse(200, "foo")); + + underTest.getAccessToken(testLwaAccessTokenRequestMeta); + + Request actualRequest = requestArgumentCaptor.getValue(); + assertEquals(TEST_ENDPOINT, actualRequest.url().toString()); + assertEquals("POST", actualRequest.method().toUpperCase()); + + Buffer bodyBuffer = new Buffer(); + actualRequest.body().writeTo(bodyBuffer); + JsonObject bodyJson = new JsonParser() + .parse(new InputStreamReader(bodyBuffer.inputStream())) + .getAsJsonObject(); + + if (sellerType == SELLER_TYPE_SELLER) { + assertEquals("rToken", bodyJson.get("refresh_token") + .getAsString()); + } + else if (sellerType == SELLER_TYPE_SELLERLESS){ + assertNotNull(bodyJson.get("scope")); + } + assertEquals("cId", bodyJson.get("client_id").getAsString()); + assertEquals("cSecret", bodyJson.get("client_secret").getAsString()); + assertEquals(EXPECTED_MEDIA_TYPE, actualRequest.body().contentType()); + } + + @ParameterizedTest + @MethodSource("lwaClient") + public void returnAccessTokenFromResponse(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + when(mockOkHttpClient.newCall(any(Request.class))) + .thenReturn(mockCall); + when(mockCall.execute()) + .thenReturn(buildResponse(200, "Azta|foo")); + + assertEquals("Azta|foo", underTest.getAccessToken(testLwaAccessTokenRequestMeta)); + } + + @ParameterizedTest + @MethodSource("lwaClient") + public void unsuccessfulPostThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + when(mockOkHttpClient.newCall(any(Request.class))) + .thenReturn(mockCall); + when(mockCall.execute()) + .thenReturn(buildResponse(400, "Azta|foo")); + + Assertions.assertThrows(RuntimeException.class, () -> { + underTest.getAccessToken(testLwaAccessTokenRequestMeta); + }); + } + + @ParameterizedTest + @MethodSource("lwaClient") + public void missingAccessTokenInResponseThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + when(mockOkHttpClient.newCall(any(Request.class))) + .thenReturn(mockCall); + when(mockCall.execute()) + .thenReturn(buildResponse(200, "")); + + Assertions.assertThrows(RuntimeException.class, () -> { + underTest.getAccessToken(testLwaAccessTokenRequestMeta); + }); + } + + private static Response buildResponse(int code, String accessToken) { + ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE, + String.format("{%s:%s}", "access_token", accessToken)); + + return new Response.Builder() + .request(new Request.Builder().url(TEST_ENDPOINT).build()) + .code(code) + .body(responseBody) + .protocol(Protocol.HTTP_1_1) + .message("OK") + .build(); + } +} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java new file mode 100644 index 0000000..f370944 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java @@ -0,0 +1,236 @@ +package com.amazon.SellingPartnerAPIAA; + +import com.amazonaws.http.HttpMethodName; +import com.squareup.okhttp.HttpUrl; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class SignableRequestImplTest { + private Request testRequest; + private SignableRequestImpl underTest; + + @Before + public void init() { + testRequest = new Request.Builder() + .url("http://www.amazon.com/request/library?test=true&sky=blue&right=右") + .get() + .build(); + + underTest = new SignableRequestImpl(testRequest); + } + + @Test + public void getHttpMethod() { + assertEquals(HttpMethodName.GET, underTest.getHttpMethod()); + + underTest = new SignableRequestImpl(new Request.Builder() + .url("https://www.amazon.com") + .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{\"foo\": \"bar\"}")) + .build()); + + assertEquals(HttpMethodName.POST, underTest.getHttpMethod()); + } + + @Test + public void getOriginalRequestObject() { + Request actualRequest = (Request)underTest.getOriginalRequestObject(); + + assertNotSame(testRequest, actualRequest); + assertEquals(testRequest.method(), actualRequest.method()); + assertEquals(testRequest.url(), actualRequest.url()); + assertEquals(testRequest.headers().toMultimap(), actualRequest.headers().toMultimap()); + assertEquals(testRequest.body(), actualRequest.body()); + } + + @Test + public void getReadLimitInfo() { + assertNull(underTest.getReadLimitInfo()); + } + + @Test + public void getResourcePath() { + assertEquals("/request/library", underTest.getResourcePath()); + } + + @Test + public void noTimeOffset() { + assertEquals(0, underTest.getTimeOffset()); + } + + @Test + public void getEndpoint() { + assertEquals(URI.create("http://www.amazon.com"), underTest.getEndpoint()); + } + + @Test + public void headers() { + Map expectedHeaders = new HashMap<>(); + + assertTrue(underTest.getHeaders().isEmpty()); + + underTest.addHeader("foo", "bar"); + expectedHeaders.put("foo", "bar"); + assertEquals(expectedHeaders, underTest.getHeaders()); + + underTest.addHeader("ban", "bop"); + expectedHeaders.put("ban", "bop"); + assertEquals(expectedHeaders, underTest.getHeaders()); + } + + @Test + public void getParameters() { + Map> expectedParamters = new HashMap<>(); + expectedParamters.put("test", Collections.singletonList("true")); + expectedParamters.put("sky", Collections.singletonList("blue")); + expectedParamters.put("right", Collections.singletonList("右")); + + assertEquals(expectedParamters, underTest.getParameters()); + } + + @Test + public void getContent() { + String expectedContent = "{\"foo\":\"bar\"}"; + StringBuilder actualContent = new StringBuilder(); + + underTest = new SignableRequestImpl(new Request.Builder() + .url("https://www.amazon.com") + .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), expectedContent)) + .build()); + + try(Scanner scanner = new Scanner(underTest.getContent())){ + while(scanner.hasNext()) { + actualContent.append(scanner.next()); + } + } + + assertEquals(expectedContent, actualContent.toString()); + } + + @Test + public void getUnwrappedContent() { + String expectedContent = "{\"ban\":\"bop\"}"; + StringBuilder actualContent = new StringBuilder(); + + underTest = new SignableRequestImpl(new Request.Builder() + .url("https://www.amazon.com") + .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), expectedContent)) + .build()); + + try(Scanner scanner = new Scanner(underTest.getContentUnwrapped())){ + while(scanner.hasNext()) { + actualContent.append(scanner.next()); + } + } + + assertEquals(expectedContent, actualContent.toString()); + } + + @Test(expected = UnsupportedOperationException.class) + public void setContentNotSupported() { + underTest.setContent(new ByteArrayInputStream("abc".getBytes())); + } + + @Test + public void addParameter() { + underTest.addParameter("left", "左"); + + HttpUrl actualHttpUrl = ((Request) underTest.getOriginalRequestObject()) + .httpUrl(); + + assertEquals(Collections.singletonList("true"), actualHttpUrl.queryParameterValues("test")); + assertEquals(Collections.singletonList("blue"), actualHttpUrl.queryParameterValues("sky")); + assertEquals(Collections.singletonList("右"), actualHttpUrl.queryParameterValues("right")); + assertEquals(Collections.singletonList("左"), actualHttpUrl.queryParameterValues("left")); + } + + @Test + public void gracefulBlankParametersParse() { + testRequest = new Request.Builder() + .url("http://www.amazon.com/request/library? ") + .get() + .build(); + + underTest = new SignableRequestImpl(testRequest); + + assertTrue(underTest.getParameters().isEmpty()); + } + + @Test + public void gracefulIncompleteParameterPairsParse() { + testRequest = new Request.Builder() + .url("http://www.amazon.com/request/library?isSigned& =false") + .get() + .build(); + + Map> expected = new HashMap<>(); + expected.put("isSigned", Collections.singletonList(null)); + expected.put(" ", Collections.singletonList("false")); + + underTest = new SignableRequestImpl(testRequest); + + assertEquals(expected, underTest.getParameters()); + } + + @Test + public void getHeadersIncludesContentTypeFromRequestBody() { + String expected = "application/json; charset=utf-8"; + RequestBody requestBody = RequestBody.create(MediaType.parse(expected), + "{\"foo\":\"bar\"}"); + + testRequest = new Request.Builder() + .url("http://www.amazon.com") + .post(requestBody) + .header("Content-Type", "THIS SHOULD BE OVERRIDDEN WITH REQUEST BODY CONTENT TYPE") + .build(); + + underTest = new SignableRequestImpl(testRequest); + + assertEquals(expected, underTest.getHeaders().get("Content-Type")); + } + + @Test + public void missingRequestBodyDoesNotOverwriteExistingContentTypeHeader() { + String expected = "testContentType"; + + testRequest = new Request.Builder() + .url("http://www.amazon.com") + .get() + .header("Content-Type", expected) + .build(); + + underTest = new SignableRequestImpl(testRequest); + + assertEquals(expected, underTest.getHeaders().get("Content-Type")); + } + + @Test + public void missingRequestBodyContentTypeDoesNotOverwriteExistingContentTypeHeader() { + String expected = "testContentType"; + + testRequest = new Request.Builder() + .url("http://www.amazon.com") + .post(RequestBody.create(null, "foo")) + .header("Content-Type", expected) + .build(); + + underTest = new SignableRequestImpl(testRequest); + + assertEquals(expected, underTest.getHeaders().get("Content-Type")); + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/.gitignore b/clients/sellingpartner-api-frp-helper-java/.gitignore new file mode 100644 index 0000000..3a576f3 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/.gitignore @@ -0,0 +1,11 @@ +.gradle/ +.idea/ +/build/ +/buildSrc +/gradle/ +/gradlew +/gradlew.bat +/wrapper/ +/target +build.gradle.backup +settings.gradle.backup \ No newline at end of file diff --git a/clients/sellingpartner-api-frp-helper-java/README.md b/clients/sellingpartner-api-frp-helper-java/README.md new file mode 100644 index 0000000..0a1e9fe --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/README.md @@ -0,0 +1,116 @@ +#Selling Partner API for Feeds, Uploads and Reports Java Helper + +This library provides helper classes for use with the Selling Partner APIs for Feeds, Uploads and Reports. It is intended for use with the Selling Partner API Client Libraries generated by [Swagger Codegen](https://swagger.io/tools/swagger-codegen/) using the OkHttp library. + +Use this library to encrypt files to be uploaded (Feeds and Uploads), or decrypt files after downloading (Reports) + +## Resources + +To use this library you will need to integrate it with one or more of the following packages: + +##### Selling Partner API Authentication/Authorization Library + +This package is required because it handles the Selling Partner API credentials required by the APIs. You can find it on our GitHub Repository [here](https://github.com/amzn/amazon-marketplace-api-sdk/tree/master/clients/sellingpartner-api-aa-java). + +##### Selling Partner API for Feeds + +The JSON file can be obtained [here](https://github.com/amzn/amazon-marketplace-api-sdk/tree/master/Feeds%20API). + +##### Selling Partner API for Uploads + +The JSON file can be obtained [here](https://github.com/amzn/amazon-marketplace-api-sdk/tree/master/Uploads%20API). + +##### Selling Partner API for Reports + +The JSON file can be obtained [here](https://github.com/amzn/amazon-marketplace-api-sdk/tree/master/Reports%20API). + +## Building + +#### Building the Client Libraries + +- Build the client library for each of the API model JSON files using Swagger Codegen. For each file: + +*For example:* + + java -jar C:\SwaggerToCL\swagger-codegen-cli.jar generate -i C:\SwaggerToCL\Feeds.json -l java -t [path to clients\sellingpartner-apiaa-java directory]/resources/swagger-codegen/templates/ -o C:\SwaggerToCL\Feeds_JavaCL + +#### Integrating the AA Library to the Helper via Maven + +1. Build the AA Library and add it as a dependency of the SDKs: + + Navigate to the **clients/sellingpartner-api-aa-java** folder and run: + + mvn package + + This generates a folder named "target". In this folder is a JAR file named **sellingpartnerapi-aa-java-1.0-jar-with-dependencies.jar** (or something similar) and all of the required dependencies. + +2. Install the JAR file in your local Maven repository: + + *For example:* + + mvn install:install-file -Dfile=[path to JAR file in "target" folder] -DgroupId=com.amazon.sellingpartnerapi -DartifactId=sellingpartnerapi-aajava -Dversion=1.0 -Dpackaging=jar + + Keep track of the *groupId*, *artifactId*, and *version* values (which can also be found near the top of the **pom.xml** file in the **clients/sellingpartner-api-aa-java** folder) because you will use them in the steps to follow. + +3. Add a dependency on the AA library in the **pom.xml** of each client library. + + *For example:* + + ``` + + com.amazon.sellingpartnerapi + sellingpartnerapi-aa-java + 1.0 + + ``` +4. Build each client library with: + + ``` + mvn package + ``` + +5. Install the Feeds, Uploads and Reports client libraries in your local Maven repository. + + *For example:* + + mvn install:install-file -Dfile=[path to JAR file in "target" folder] -DgroupId=com.amazon.sellingpartnerapi -DartifactId=sellingpartnerapi-feeds-java -Dversion=1.0 -Dpackaging=jar + +6. Confirm that the *artifactId* of the Feeds, Uploads and Reports libraries in the **pom.xml** of the Helper matches the ones used when installing each library. + + *For example:* + + ``` + + com.amazon.sellingpartnerapi + sellingpartnerapi-feeds-java + 1.0 + + ``` + +7. Navigate to the **SellingPartnerApiFeedsUploadsReportsJavaHelper** folder and run: + + ``` + mvn package + ``` + *The Feeds, Uploads and Reports Java Helper SDK comes with two building options: Maven and Gradle, however, we only detail how to complete the Maven build above.* + +## License +Swagger Codegen templates are subject to the [Swagger Codegen License](https://github.com/swagger-api/swagger-codegen#license). + +All other work licensed as follows: + +Copyright Amazon.com Inc. or its affiliates. + +All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this library except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/clients/sellingpartner-api-frp-helper-java/build.gradle b/clients/sellingpartner-api-frp-helper-java/build.gradle new file mode 100644 index 0000000..b38dabc --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/build.gradle @@ -0,0 +1,107 @@ + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.+' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + } +} + +plugins { + id 'idea' + id 'eclipse' + id 'java' + id 'maven' +} + +group = 'io.swagger' +version = '1.0.0' + + +repositories { + jcenter() +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + + + +/*sourceSets { + main { + java { + srcDirs = ['src/main/java'] + } + resources { + srcDirs = ['src/main/resources'] + } + } + + test { + java { + srcDirs = ['src/test/java'] + } + resources { + srcDirs = ['src/test/resources'] + } + } +}*/ + + +install { + repositories.mavenInstaller { + pom.artifactId = 'feeds-uploads-reports-java-helper' + } +} + +task execute(type: JavaExec) { + main = System.getProperty('mainClass') + classpath = sourceSets.main.runtimeClasspath +} + +test { + jvmArgs('-Djavax.net.ssl.trustStore=resources/main/certs/InternalAndExternalTrustStore.jks', + '-Djavax.net.ssl.trustStorePassword=amazon') +} + +test.dependsOn processTestResources + +// TO get this to work, +// You must add the sellingpartner-api-aa-java.jar (with dependencies) to your +// /libs/ folder!!!!! +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.8' + annotationProcessor 'org.projectlombok:lombok:1.18.8' + implementation 'io.swagger:swagger-annotations:1.5.17' + implementation 'com.squareup.okhttp:okhttp:2.7.5' + implementation 'com.squareup.okhttp:logging-interceptor:2.7.5' + implementation 'com.google.code.gson:gson:2.8.1' + implementation 'io.gsonfire:gson-fire:1.8.0' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.10.2' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.10.2' + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.10.2' + implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.12' + implementation group: 'org.apache.directory.studio', name: 'org.apache.commons.io', version: '2.4' + implementation group: 'com.google.guava', name: 'guava', version: '28.2-jre' + implementation group: 'joda-time', name: 'joda-time', version: '2.10.5' + + // https://mvnrepository.com/artifact/org.threeten/threetenbp + implementation group: 'org.threeten', name: 'threetenbp', version: '1.3.5' + // https://mvnrepository.com/artifact/junit/junit + testCompile group: 'junit', name: 'junit', version: '4.12' + + implementation 'com.amazonaws:aws-java-sdk-signer:1.11.610' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.squareup.okhttp:okhttp:2.7.5' + implementation 'org.junit.jupiter:junit-jupiter-engine:5.0.0' + implementation 'org.junit.jupiter:junit-jupiter-params:5.3.2' + implementation 'org.junit.jupiter:junit-jupiter-migrationsupport:5.5.1' + implementation 'org.mockito:mockito-core:3.0.0' + implementation 'org.apache.commons:commons-lang3:3.9' + implementation 'org.apache.httpcomponents:httpclient:4.5.9' + + implementation fileTree(dir: 'libs', include: '*.jar') + +} diff --git a/clients/sellingpartner-api-frp-helper-java/libs/sellingpartner-api-aa-java-1.0.jar b/clients/sellingpartner-api-frp-helper-java/libs/sellingpartner-api-aa-java-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..31fd121dcf7f3e3943ccd33a14ccb14351ba70c4 GIT binary patch literal 21917 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 PD{%V@P_4O!q`&$bN|)g! literal 0 HcmV?d00001 diff --git a/clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-feeds-java-1.0.jar b/clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-feeds-java-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..36eee170a418e4450cd80983345f27c509152a36 GIT binary patch literal 103341 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

yMfl`4TL%HqHk?*m<=523fzYNb2SAFv+`mU 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 literal 0 HcmV?d00001 diff --git a/clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-reports-java-1.0.jar b/clients/sellingpartner-api-frp-helper-java/libs/sellingpartnerapi-reports-java-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..b24866c8bffd9e27f1f1f58eb813107bd57f58be GIT binary patch literal 158402 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%0DdQ>D71=Up41lfsxb)Py{VFQd6%q08 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 L58nVD6x9C%O4&e2 literal 0 HcmV?d00001 diff --git a/clients/sellingpartner-api-frp-helper-java/pom.xml b/clients/sellingpartner-api-frp-helper-java/pom.xml new file mode 100644 index 0000000..6115587 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/pom.xml @@ -0,0 +1,164 @@ + + + 4.0.0 + io.swagger + feeds-uploads-reports-java-helper + 1.0.0 + + + org.projectlombok + lombok + 1.18.12 + provided + + + io.swagger + swagger-annotations + 1.5.17 + + + com.squareup.okhttp + okhttp + 2.7.5 + + + com.squareup.okhttp + logging-interceptor + 2.7.5 + + + io.gsonfire + gson-fire + 1.8.0 + + + com.fasterxml.jackson.core + jackson-core + 2.10.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.10.2 + + + com.fasterxml.jackson.core + jackson-annotations + 2.10.2 + + + org.apache.httpcomponents + httpclient + 4.5.12 + + + org.apache.directory.studio + org.apache.commons.io + 2.4 + + + com.google.guava + guava + 28.2-jre + + + joda-time + joda-time + 2.10.5 + + + org.threeten + threetenbp + 1.3.5 + + + com.amazonaws + aws-java-sdk-signer + 1.11.610 + + + com.google.code.gson + gson + 2.8.5 + + + org.junit.jupiter + junit-jupiter-engine + 5.0.0 + + + org.junit.jupiter + junit-jupiter-params + 5.3.2 + + + org.junit.jupiter + junit-jupiter-migrationsupport + 5.5.1 + + + org.mockito + mockito-core + 3.0.0 + + + org.apache.commons + commons-lang3 + 3.9 + + + junit + junit + 4.12 + test + + + com.amazon.sellingpartnerapi + sellingpartnerapi-aa-java + 1.0 + + + com.amazon.sellingpartnerapi + sellingpartnerapi-feeds-java + 1.0 + + + com.amazon.sellingpartnerapi + sellingpartnerapi-reports-java + 1.0 + + + com.amazon.sellingpartnerapi + sellingpartnerapi-uploads-java + 1.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + + + org.projectlombok + lombok + 1.18.12 + + + + + + + + + 1.8 + ${java.version} + ${java.version} + 1.0.0 + UTF-8 + + diff --git a/clients/sellingpartner-api-frp-helper-java/settings.gradle b/clients/sellingpartner-api-frp-helper-java/settings.gradle new file mode 100644 index 0000000..5b80a16 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "feeds-uploads-reports-java-helper" \ No newline at end of file diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DocumentValidatorInputStream.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DocumentValidatorInputStream.java new file mode 100644 index 0000000..ba6caeb --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DocumentValidatorInputStream.java @@ -0,0 +1,64 @@ +package com.amazon.spapi.sequencing.client; + +import com.google.common.base.Preconditions; +import lombok.extern.apachecommons.CommonsLog; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +/** + * An input stream that validates the integrity of the document when you close the stream. + */ +@CommonsLog +public class DocumentValidatorInputStream extends FilterInputStream { + + private final byte[] expectedSha256sum; + private final MessageDigest sha256digest; + + /** + * Creates an InputStream that will calculate the sha256digest and validate it upon closing the stream. + * @param inputStream + * the inputStream to calculate the sha256sum of. + * @param expectedSha256sum + * the expected sha256sum of the document being streamed if one exists. Leave null if document does not + * have a sha256sum to validate against. + * @throws NoSuchAlgorithmException + * when unable to create a SHA-256 instance of MessageDigest + */ + public DocumentValidatorInputStream(InputStream inputStream, byte[] expectedSha256sum) + throws NoSuchAlgorithmException { + super(null); + Preconditions.checkArgument(inputStream != null, "inputStream must not be null"); + Preconditions.checkArgument(expectedSha256sum != null, "expectedSha256sum must not be null"); + + this.expectedSha256sum = Arrays.copyOf(expectedSha256sum, expectedSha256sum.length); + sha256digest = MessageDigest.getInstance("SHA-256"); + this.in = new DigestInputStream(inputStream, sha256digest); + } + + /** + * Validates that the streamed document was not tampered with. + * @throws IOException + * if the streamed document was completely read and is not valid or an IOException occurs while closing + * the stream. + */ + @Override + public void close() throws IOException { + try { + if (!MessageDigest.isEqual(expectedSha256sum, sha256digest.digest()) && super.read() == -1) { + throw new ValidationFailureException("The streamed document was not valid."); + } + } finally { + try { + super.close(); + } catch (IOException e) { + log.warn(String.format("Exception while closing the validation string: %s", e.getMessage()), e); + } + } + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DownloadsSequencer.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DownloadsSequencer.java new file mode 100644 index 0000000..84967ca --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/DownloadsSequencer.java @@ -0,0 +1,23 @@ +package com.amazon.spapi.sequencing.client; + +import com.amazon.spapi.sequencing.crypto.EncryptionException; +import io.swagger.client.model.EncryptionDetails; +import io.swagger.client.model.UploadDestination; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public interface DownloadsSequencer { + + InputStream downloadAndDecryptReportContent(UploadDestination destination, boolean isGzipped) throws IOException; + + InputStream decryptReportContent(File reportFile, EncryptionDetails details, boolean isGzipped) + throws IOException, EncryptionException; + + void downloadDecryptThenWriteFile(UploadDestination destination, boolean isGzipped, File fileToWriteTo) + throws IOException; + + void decryptThenWriteFile(File reportFile, EncryptionDetails details, boolean isGzipped, File fileToWriteTo) + throws IOException, EncryptionException; +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/SpoolingLimitExceededException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/SpoolingLimitExceededException.java new file mode 100644 index 0000000..f3522fc --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/SpoolingLimitExceededException.java @@ -0,0 +1,19 @@ +package com.amazon.spapi.sequencing.client; + +import java.io.IOException; + +/** + * Exception thrown when spooling size limits exceeded. + */ +public class SpoolingLimitExceededException extends IOException { + + /** + * Constructor that takes in a message. + * @param message + * the message describing the exception + */ + public SpoolingLimitExceededException(String message) { + super(message); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadDetails.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadDetails.java new file mode 100644 index 0000000..2fa10cb --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadDetails.java @@ -0,0 +1,19 @@ +package com.amazon.spapi.sequencing.client; + + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import lombok.experimental.FieldDefaults; + +@Builder +@Getter +@ToString +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class UploadDetails { + String sha256Sum; + String uploadDestinationId; +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadsSequencer.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadsSequencer.java new file mode 100644 index 0000000..8ed6b52 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/UploadsSequencer.java @@ -0,0 +1,46 @@ +package com.amazon.spapi.sequencing.client; + +import io.swagger.client.ApiException; +import io.swagger.client.model.UploadDestination; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; + +/** + * A thick client used to simplify the use of SP-API Uploads. + */ +public interface UploadsSequencer { + /** + * Retrieve a document and related metadata from SP-API Uploads. + * @param obfuscatedCustomerId the obfuscated customer ID of the document owner. + * @param documentId the document ID of the document being retrieved. + * @return A DownloadBundle to be used to retrieve the requested document. + * @throws RuntimeException if an exceptional case was encountered but the action should be retried. + * @throws RuntimeException if an exceptional case was encountered and the action should not be retried. + * @throws RuntimeException if an exceptional case was encountered and the cause was unexpected. + */ + // DownloadBundle get(String obfuscatedCustomerId, String documentId) throws RuntimeException; + + UploadDestination createDestination(String feedType, String contentType, long documentLength) + throws ApiException; + /** + * Delivers a document and related metadata to SP-API Uploads. + * @param uploadDestination metadata for the document being uploaded. + * @param contentType the content type + * @param documentLength the length in bytes of the document. + * @param inputStream the InputStream from which the document should be read for upload. + * @return A String document ID. + * @throws RuntimeException if an exceptional case was encountered but the action should be retried. + * @throws RuntimeException if an exceptional case was encountered and the action should not be retried. + * @throws RuntimeException if an exceptional case was encountered and the cause was unexpected. + */ + UploadDetails uploadToDestination(UploadDestination uploadDestination, String contentType, long documentLength, + InputStream inputStream) throws RuntimeException; + + UploadDetails createDestinationAndUpload(String feedType, String contentType, File fileName) + throws ApiException, FileNotFoundException; + + UploadDetails createDestinationAndUpload(String feedType, String contentType, long documentLength, + InputStream stream) throws ApiException; +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/ValidationFailureException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/ValidationFailureException.java new file mode 100644 index 0000000..4957841 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/ValidationFailureException.java @@ -0,0 +1,38 @@ +package com.amazon.spapi.sequencing.client; + + +/** + * Exception thrown if document integrity has been compromised during handling and is not considered safe to consume. + */ +public class ValidationFailureException extends RuntimeException { + + /** + * Constructor taking both a message and cause. + * @param message + * the message + * @param cause + * the cause + */ + public ValidationFailureException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message + * the message + */ + public ValidationFailureException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause + * the cause + */ + public ValidationFailureException(Throwable cause) { + super(cause); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/AccessMechanism.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/AccessMechanism.java new file mode 100644 index 0000000..167743d --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/AccessMechanism.java @@ -0,0 +1,15 @@ +package com.amazon.spapi.sequencing.client.impl; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@AllArgsConstructor +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@Getter +public enum AccessMechanism { + UPLOAD("upload"), DOWNLOAD("download"); + + private String description; +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/DownloadsSequencerImpl.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/DownloadsSequencerImpl.java new file mode 100644 index 0000000..6bc524a --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/DownloadsSequencerImpl.java @@ -0,0 +1,307 @@ +package com.amazon.spapi.sequencing.client.impl; + +import com.amazon.spapi.sequencing.client.DownloadsSequencer; +import com.amazon.spapi.sequencing.crypto.EncryptionException; +import com.google.common.base.Strings; + +import io.swagger.client.model.EncryptionDetails; +import io.swagger.client.model.UploadDestination; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.experimental.FieldDefaults; +import lombok.extern.apachecommons.CommonsLog; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.util.EntityUtils; + +import javax.crypto.Cipher; +import java.io.*; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; + +import static com.amazon.spapi.sequencing.client.impl.SequencerHelper.S3_IAD_ENDPOINT; +import static com.amazon.spapi.sequencing.client.impl.SequencerHelper.S3_SEA_ENDPOINT; + +@Builder +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@CommonsLog +public class DownloadsSequencerImpl implements DownloadsSequencer { + @Builder.Default + private final SequencerHelper helper = SequencerHelper.defaultSequencerHelper(); + + @Override + public InputStream downloadAndDecryptReportContent(UploadDestination destination, boolean isGzipped) { + boolean hasError = false; + InputStream result = null; + String truncatedUrl = null; + try { + + String url = destination.getUrl(); + + if (Strings.isNullOrEmpty(url)) { + throw new RuntimeException("The returned download URL is null or empty."); + } + + // Acquire the file + HttpClient httpClient = helper.getHttpClientFactory().newGetClient(); + HttpGet httpGet = new HttpGet(url); + + truncatedUrl = helper.removeQueryFromUrl(url); + log.info(String.format("Downloading document with destination ID %s using URL %s (signing info redacted)", + destination.getUploadDestinationId(), truncatedUrl + )); + HttpResponse httpResponse = httpClient.execute(httpGet); + if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) { + boolean isS3UsStandardRegion = false; + if (url.contains(S3_IAD_ENDPOINT)) { + isS3UsStandardRegion = true; + url = url.replaceFirst(Pattern.quote(S3_IAD_ENDPOINT), S3_SEA_ENDPOINT); + } else if (url.contains(S3_SEA_ENDPOINT)) { + isS3UsStandardRegion = true; + url = url.replaceFirst(Pattern.quote(S3_SEA_ENDPOINT), S3_IAD_ENDPOINT); + } + + if (isS3UsStandardRegion) { + log.info("S3 Returned a 404 in the US Standard Region." + + " This likely indicates issues with eventual consistency and should be rare." + + " Attempting to find object in peer region." + + " See: https://w.amazon.com/index.php/S3#What_About_Eventual_Consistency.3F"); + + // Consume the previous response so that the HTTP client connection can be reused + EntityUtils.consumeQuietly(httpResponse.getEntity()); + + httpGet = new HttpGet(url); + httpResponse = httpClient.execute(httpGet); + } + } + + // Handle error responses + helper.handleErrorResponseCodes(httpResponse, AccessMechanism.DOWNLOAD); + + HttpEntity entity = httpResponse.getEntity(); + if (entity == null) { + throw new RuntimeException("The HTTP store returned success but no document."); + } + result = entity.getContent(); + result = decryptReportContent(entity.getContent(), destination.getEncryptionDetails(), isGzipped); + return result; + } catch (ClientProtocolException e) { + // Indicates an error in the HTTP protocol + hasError = true; + throw new RuntimeException( + "Unable to download the file.", e); + } catch (EncryptionException e) { + // Unable to perform cipher operations + hasError = true; + throw new RuntimeException( + "Unable to perform cipher operations.", e); + } catch (IOException e) { + hasError = true; + // Unable to download the file or operate on the stream + throw new RuntimeException( + String.format("Unable to download the file using URL %s (signing info redacted) " + + "or manipulate the InputStream.", truncatedUrl), + e + ); + } finally { + // kill the stream if we are throwing an exception + if (hasError) { + IOUtils.closeQuietly(result); + } + } + } + + @Override + public InputStream decryptReportContent(File reportFile, EncryptionDetails details, boolean isGzipped) + throws IOException, EncryptionException { + return decryptReportContent(new FileInputStream(reportFile), details, isGzipped); + } + + private InputStream decryptReportContent(InputStream input, EncryptionDetails details, boolean isGzipped) + throws EncryptionException, IOException { + InputStream resultStream = input; + // If encrypted, decipher the stream + if (details != null && EncryptionDetails.StandardEnum.AES.equals(details.getStandard())) { + try { + resultStream = helper.buildCipherInputStream(details, resultStream, Cipher.DECRYPT_MODE); + + } catch (IllegalArgumentException | IllegalStateException e) { + throw new EncryptionException(e); + } + } + + // Determine if the stream should be unzipped as well + if (isGzipped) { + resultStream = new GZIPInputStream(resultStream); + } + + return resultStream; + } + + @Override + public void downloadDecryptThenWriteFile(UploadDestination destination, boolean isGzipped, File fileToWriteTo) + throws IOException { + try ( + InputStream result = downloadAndDecryptReportContent(destination, isGzipped); + FileOutputStream output = new FileOutputStream(fileToWriteTo); + ) { + IOUtils.copyLarge(result, output); + } + } + + @Override + public void decryptThenWriteFile(File reportFile, EncryptionDetails details, boolean isGzipped, + File fileToWriteTo) throws IOException, EncryptionException { + try ( + InputStream result = decryptReportContent(reportFile,details, isGzipped); + FileOutputStream output = new FileOutputStream(fileToWriteTo); + ) { + IOUtils.copyLarge(result, output); + } + + } + + /* + + + + */ + /** {@inheritDoc} *//* + + @Override + public DownloadBundle get(String obfuscatedCustomerId, String documentId) + throws RuntimeException, RuntimeException, RuntimeException { + Preconditions.checkArgument(!Strings.isNullOrEmpty(obfuscatedCustomerId), + "obfuscatedMerchantCustomerId is null or empty."); + Preconditions.checkArgument(!Strings.isNullOrEmpty(documentId), "documentId is null or empty."); + Preconditions.checkState(httpClientFactory != null, "The httpClientFactory is null."); + + // We need the expanded scope so that we can close the stream in the case of an exception. + InputStream resultStream = null; + DownloadBundle result = null; + String truncatedUrl = null; + try { + // Acquire the document metadata, document receipt URL and encryption details from Tortuga. + GetDownloadUrlInput getUrlInput = new GetDownloadUrlInput(); + getUrlInput.setDocumentId(documentId); + getUrlInput.setObfuscatedCustomerId(obfuscatedCustomerId); + GetDownloadUrlOutput getUrlOutput = + GetDownloadUrlSyncJobBuilder.build(getUrlInput, metricsStrategy, client).run(); + DocumentMetadata metadata = getUrlOutput.getDocumentMetadata(); + if (metadata == null) { + throw new RuntimeException("The returned document metadata is null."); + } + String url = getUrlOutput.getUrl(); + if (Strings.isNullOrEmpty(url)) { + throw new RuntimeException("The returned download URL is null or empty."); + } + Date creationTimestamp = getUrlOutput.getCreationTimestamp(); + if (creationTimestamp == null) { + throw new RuntimeException("The returned document creationDate is null."); + } + + getUrlOutput.setDocumentMetadata(null); + + // Acquire the file + HttpClient httpClient = httpClientFactory.newGetClient(); + HttpGet httpGet = new HttpGet(url); + + truncatedUrl = removeQueryFromUrl(url); + log.info(String.format("Downloading document with ID %s using URL %s (signing info redacted)", + documentId, truncatedUrl)); + long startTimeMillis = System.nanoTime() / 1000000L; + HttpResponse httpResponse = httpClient.execute(httpGet); + if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) { + boolean isS3UsStandardRegion = false; + if (url.contains(S3_IAD_ENDPOINT)) { + isS3UsStandardRegion = true; + url = url.replaceFirst(Pattern.quote(S3_IAD_ENDPOINT), S3_SEA_ENDPOINT); + } else if (url.contains(S3_SEA_ENDPOINT)) { + isS3UsStandardRegion = true; + url = url.replaceFirst(Pattern.quote(S3_SEA_ENDPOINT), S3_IAD_ENDPOINT); + } + + if (isS3UsStandardRegion) { + log.info("S3 Returned a 404 in the US Standard Region." + + " This likely indicates issues with eventual consistency and should be rare." + + " Attempting to find object in peer region." + + " See: https://w.amazon.com/index.php/S3#What_About_Eventual_Consistency.3F"); + + // Consume the previous response so that the HTTP client connection can be reused + EntityUtils.consumeQuietly(httpResponse.getEntity()); + + httpGet = new HttpGet(url); + httpResponse = httpClient.execute(httpGet); + } + } + long endTimeMillis = System.nanoTime() / 1000000L; + serviceMetrics.addTime(S3_GET_TIME_METRIC, endTimeMillis - startTimeMillis, SI.MILLI(SI.SECOND)); + + // Handle error responses + handleErrorResponseCodes(httpResponse); + + HttpEntity entity = httpResponse.getEntity(); + if (entity == null) { + throw new RuntimeException("The HTTP store returned success but no document."); + } + resultStream = entity.getContent(); + + // If this document has a sha256sum in the metadata, wrap with DigestInputStream to calculate the sha256 sum + // for validation of document integrity + String sha256sumStr = getUrlOutput.getSha256sum(); + if (sha256sumStr != null) { + resultStream = new DocumentValidatorInputStream(resultStream, BASE64_DECODER.decode(sha256sumStr)); + } + + // If encrypted, decipher the stream + if (getUrlOutput.isEncrypted()) { + try { + EncryptionInfo encryptionInfo = getUrlOutput.getEncryptionInfo(); + resultStream = buildCipherInputStream(encryptionInfo, resultStream, Cipher.DECRYPT_MODE); + + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalStateException e) { + throw new RuntimeException(e); + } + } + + // Determine if the stream should be unzipped as well + if (metadata.isDocumentGzipped()) { + resultStream = new GZIPInputStream(resultStream); + } + + // build the result + result = new DownloadBundle(metadata, resultStream, creationTimestamp.getTime()); + } catch (ClientProtocolException e) { + // Indicates an error in the HTTP protocol + throw new RuntimeException( + "Unable to download the file.", e); + } catch (EncryptionException e) { + // Unable to perform cipher operations + throw new RuntimeException( + "Unable to perform cipher operations.", e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Couldn't get a Sha256 Message Digest instance", e); + } catch (IOException e) { + // Unable to download the file or operate on the stream + throw new RuntimeException( + String.format("Unable to download the file using URL %s (signing info redacted) " + + "or manipulate the InputStream.", truncatedUrl), + e); + } finally { + // kill the stream if we are throwing an exception + if (result == null) { + IOUtils.closeQuietly(resultStream); + } + } + return result; + } +*/ + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/HttpClientFactory.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/HttpClientFactory.java new file mode 100644 index 0000000..6ac32e7 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/HttpClientFactory.java @@ -0,0 +1,19 @@ +package com.amazon.spapi.sequencing.client.impl; + +import org.apache.http.client.HttpClient; + +/** + * Constructs purpose built HttpClient instances. + */ +public interface HttpClientFactory { + /** + * Constructs an HttpClient instance configured for GET requests. + * @return an HttpClient instance. + */ + HttpClient newGetClient(); + /** + * Constructs an HttpClient instance configured for PUT requests. + * @return an HttpClient instance. + */ + HttpClient newPutClient(); +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SPAPIUploadDestinationsHttpClientFactory.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SPAPIUploadDestinationsHttpClientFactory.java new file mode 100644 index 0000000..ac8d225 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SPAPIUploadDestinationsHttpClientFactory.java @@ -0,0 +1,71 @@ +package com.amazon.spapi.sequencing.client.impl; + +import com.google.common.collect.ImmutableMap; +import lombok.Setter; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; + +import java.util.Map; + +/** + * HttpClientFactory implementation for SP-API Upload Destinations. + */ +@Setter +public class SPAPIUploadDestinationsHttpClientFactory implements HttpClientFactory { + private static final Map DEFAULT_CONNECTION_PARAMETERS; + + static { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); + builder.put(CoreConnectionPNames.SO_TIMEOUT, 10000); + builder.put(CoreConnectionPNames.SO_KEEPALIVE, Boolean.TRUE); + + DEFAULT_CONNECTION_PARAMETERS = builder.build(); + } + + /** + *http://hc.apache.org/httpcomponents-core-4.2.x/httpcore/apidocs/org/apache/http/params/CoreConnectionPNames.html. + */ + private Map getClientConnectionParametersOverrides = DEFAULT_CONNECTION_PARAMETERS; + + /** + *http://hc.apache.org/httpcomponents-core-4.2.x/httpcore/apidocs/org/apache/http/params/CoreConnectionPNames.html. + */ + private Map putClientConnectionParametersOverrides = DEFAULT_CONNECTION_PARAMETERS; + + private String userAgent; + + /** {@inheritDoc} */ + @Override + public HttpClient newGetClient() { + return newHttpClient(getClientConnectionParametersOverrides); + } + + /** {@inheritDoc} */ + @Override + public HttpClient newPutClient() { + return newHttpClient(putClientConnectionParametersOverrides); + } + + /** + * Helper function to create a new DefaultHttpClient with the given connectionParameter Overrides. + * @param connectionParameters used to override the default properties of the HttpClient. + * @return a new, configured HttpClient. + */ + private HttpClient newHttpClient(Map connectionParameters) { + // TODO: evaluate necessity + // Preconditions.checkState(!Strings.isNullOrEmpty(userAgent), "You must supply a User-Agent."); + + DefaultHttpClient client = new DefaultHttpClient(); + for (Map.Entry param : connectionParameters.entrySet()) { + client.getParams().setParameter(param.getKey(), param.getValue()); + } + + if (userAgent != null) { + client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, userAgent); + } + return client; + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SequencerHelper.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SequencerHelper.java new file mode 100644 index 0000000..45bfb5f --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/SequencerHelper.java @@ -0,0 +1,156 @@ +package com.amazon.spapi.sequencing.client.impl; + +import com.amazon.spapi.sequencing.crypto.CryptoProvider; +import com.amazon.spapi.sequencing.crypto.EncryptionException; +import com.amazon.spapi.sequencing.crypto.EncryptionMaterials; +import com.amazon.spapi.sequencing.crypto.SymmetricCryptoProvider; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import io.swagger.client.model.EncryptionDetails; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import lombok.extern.apachecommons.CommonsLog; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpResponseException; +import org.apache.http.util.EntityUtils; + +import javax.crypto.CipherInputStream; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Base64; +import java.util.Objects; + +@Builder +@Getter +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@CommonsLog +public class SequencerHelper { + + static final String SHA_256 = "SHA-256"; + static final String MD5 = "MD5"; + static final String S3_IAD_ENDPOINT = "s3-external-1.amazonaws.com"; + static final String S3_SEA_ENDPOINT = "s3-external-2.amazonaws.com"; + static final String S3_GET_TIME_METRIC = "S3GetTime"; + static final String S3_PUT_TIME_METRIC = "S3PutTime"; + static final String REQUIRED_KEY_ALGORITHM = "AES"; + static final int AES_BLOCK_SIZE = 16; + static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder(); + static final Base64.Decoder BASE64_DECODER = Base64.getDecoder(); + + private CryptoProvider cryptoProvider; + + private HttpClientFactory httpClientFactory; + + public static SequencerHelper defaultSequencerHelper() { + return SequencerHelper.builder() + .cryptoProvider(new SymmetricCryptoProvider()) + .httpClientFactory(new SPAPIUploadDestinationsHttpClientFactory()) + .build(); + } + + /** + * Remove the query string from a signed S3 URL to hide access key, signature, expiration info when logging. + * + * @param signedS3Url Signed S3 URL to truncate + * @return truncated URL + */ + String removeQueryFromUrl(String signedS3Url) { + if (signedS3Url == null) { + return null; + } + + try { + URL url = new URL(signedS3Url); + + if (url.getQuery() == null) { + return signedS3Url; + } else { + return signedS3Url.replace(url.getQuery(), ""); + } + } catch (MalformedURLException e) { + return null; + } + } + + + /** + * Wraps the provided InputStream in a CipherInputStream in DECRYPT mode. + * + * @param encryptionDetails The EncryptionInfo to use in constructing the Cipher. + * @param stream The InputStream to wrap. + * @param mode The cipher mode. + * @return The wrapped InputStream + * @throws EncryptionException if initializing the Cipher fails. + */ + InputStream buildCipherInputStream(EncryptionDetails encryptionDetails, InputStream stream, int mode) + throws EncryptionException { + Preconditions.checkArgument(!Objects.isNull(encryptionDetails), "Encryption details missing"); + Preconditions.checkArgument(!Strings.isNullOrEmpty(encryptionDetails.getInitializationVector()), + "iv is null or empty." + ); + Preconditions.checkArgument(!Strings.isNullOrEmpty(encryptionDetails.getKey()), "key is null or empty."); + Preconditions.checkState(cryptoProvider != null, "The CryptoProvider must be non-null."); + + String key = encryptionDetails.getKey(); + String iv = encryptionDetails.getInitializationVector(); + + EncryptionMaterials materials = new EncryptionMaterials(new SecretKeySpec(BASE64_DECODER.decode(key), + REQUIRED_KEY_ALGORITHM + ), BASE64_DECODER.decode(iv)); + return new CipherInputStream(stream, cryptoProvider.getInitializedCipher(mode, materials)); + } + + + /** + * Examine the HttpResponse object and throw appropriate exceptions for non-success status + * codes. + * + * @param httpResponse the HttpResponse object to examine. + * @throws RuntimeException if the status code is not 200 level or 400 level (except 403 errors). + * @throws RuntimeException if the HttpResponse, or its nested StatusLine object are null. Or if the status + * code is a 400 level HTTP response code (except 403 errors, which are retriable). + */ + void handleErrorResponseCodes(HttpResponse httpResponse, AccessMechanism accessMechanism) throws RuntimeException { + // Handle error responses + if (httpResponse == null || httpResponse.getStatusLine() == null) { + throw new RuntimeException("Unable to " + accessMechanism.getDescription() + " the document."); + } + int status = httpResponse.getStatusLine().getStatusCode(); + log.info(String.format("Document " + accessMechanism.getDescription() + " returned status code %d", status)); + int statusRange = status / 100; + if (statusRange != 2) { // Not 2XX + HttpEntity entity = httpResponse.getEntity(); + if (entity != null) { + try { + log.warn(EntityUtils.toString(entity, "UTF-8")); + } catch (IOException e) { + log.warn("Got IOException when trying to decode response body into a String", e); + } + } + String reason = httpResponse.getStatusLine().getReasonPhrase(); + HttpResponseException error = new HttpResponseException(status, reason); + // A 403 response is typically due to the signed URL expiring before use. + // The root cause of the expiration is usually clock skew, so treat this as retriable. + if (status == HttpStatus.SC_FORBIDDEN) { + throw new RuntimeException( + String.format("A 403 response was returned. This typically indicates that the signed URL " + + "expired before use (usually due to clock skew). %s %s", status, reason), + error + ); + } else if (statusRange == 4) { // 4XX + throw new RuntimeException( + String.format("A 400 level response was returned: %s %s", status, reason), error); + } else { // 1XX, 3XX, 5XX + throw new RuntimeException( + String.format("A non 200/400 level response was returned: %s %s", status, reason), error); + } + } + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/UploadsSequencerImpl.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/UploadsSequencerImpl.java new file mode 100644 index 0000000..9a09842 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/impl/UploadsSequencerImpl.java @@ -0,0 +1,178 @@ +package com.amazon.spapi.sequencing.client.impl; + +import com.amazon.spapi.sequencing.client.UploadDetails; +import com.amazon.spapi.sequencing.client.UploadsSequencer; +import com.amazon.spapi.sequencing.crypto.EncryptionException; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import io.swagger.client.ApiException; +import io.swagger.client.api.UploadsApi; +import io.swagger.client.model.CreateUploadDestinationResponse; +import io.swagger.client.model.EncryptionDetails; +import io.swagger.client.model.UploadDestination; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.experimental.FieldDefaults; +import lombok.extern.apachecommons.CommonsLog; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.InputStreamEntity; + +import javax.crypto.Cipher; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * This implementation performs the expected crypto and compression work on the provided streams. + */ +@Builder +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@CommonsLog +public class UploadsSequencerImpl implements UploadsSequencer { + + @Builder.Default + private final SequencerHelper helper = SequencerHelper.defaultSequencerHelper(); + + private UploadsApi uploadsApi; + + /** + * {@inheritDoc} + */ + @Override + public UploadDetails uploadToDestination(UploadDestination uploadDestination, String contentType, + long documentLength, + InputStream inputStream) throws RuntimeException { + Preconditions.checkArgument(uploadDestination != null, "documentMetadata is null."); + Preconditions.checkArgument(inputStream != null, "inputStream is null."); + Preconditions.checkArgument(documentLength >= 0, "documentLength must be at least zero."); + Preconditions.checkState(helper.getHttpClientFactory() != null, "The httpClientFactory is null."); + + String truncatedUrl = null; + try { + + final String uploadDestinationId = uploadDestination.getUploadDestinationId(); + if (Strings.isNullOrEmpty(uploadDestinationId)) { + throw new RuntimeException("The returned uploadDestinationId is null or empty."); + } + EncryptionDetails encryptionDetails = uploadDestination.getEncryptionDetails(); + if (encryptionDetails == null) { + throw new RuntimeException("The returned encryptionDetails is null."); + } + final String url = uploadDestination.getUrl(); + if (Strings.isNullOrEmpty(url)) { + throw new RuntimeException("The returned upload URL is null or empty."); + } + + // Wrap the stream with a CipherStream + inputStream = helper.buildCipherInputStream(encryptionDetails, inputStream, Cipher.ENCRYPT_MODE); + + // Wrap the cipher stream with a sha256 digest stream. + DigestInputStream sha256sumStream = new DigestInputStream(inputStream, MessageDigest.getInstance( + SequencerHelper.SHA_256)); + inputStream = sha256sumStream; + + truncatedUrl = helper.removeQueryFromUrl(url); + + // Put the file + HttpClient httpClient = helper.getHttpClientFactory().newPutClient(); + HttpPut httpPut = new HttpPut(url); + // This content length calculation is specific to AES + long contentLength = (documentLength / SequencerHelper.AES_BLOCK_SIZE + 1) * SequencerHelper.AES_BLOCK_SIZE; + // This sets the Content-Length header since we specified the contentLength + HttpEntity document = new InputStreamEntity(inputStream, contentLength); + httpPut.setHeader("Content-Type", contentType); + httpPut.setEntity(document); + + log.info(String.format("Uploading document with ID %s using URL %s (signing info redacted)", + uploadDestinationId, truncatedUrl + )); + HttpResponse httpResponse = httpClient.execute(httpPut); + + // Handle error responses + helper.handleErrorResponseCodes(httpResponse, AccessMechanism.UPLOAD); + log.info(String.format("Successfully uploaded document with ID %s", uploadDestinationId)); + + byte[] sha256sumDigest = sha256sumStream.getMessageDigest().digest(); + String sha256sum = SequencerHelper.BASE64_ENCODER.encodeToString(sha256sumDigest); + + + return UploadDetails.builder() + .sha256Sum(sha256sum) + .uploadDestinationId(uploadDestinationId) + .build(); + } catch (ClientProtocolException e) { + // Indicates an error in the HTTP protocol + throw new RuntimeException("Unable to upload the file.", e); + } catch (EncryptionException e) { + // Unable to perform cipher operations + throw new RuntimeException("Unable to perform cipher operations.", e); + } catch (IOException e) { + // Unable to upload the file or operate on the stream + throw new RuntimeException( + String.format("Unable to upload the file using URL %s (signing info redacted) " + + "or manipulate the InputStream.", truncatedUrl), + e + ); + } catch (NoSuchAlgorithmException e) { + // Unable to get a MAC on the encrypted doc + throw new IllegalStateException("Unable to create MessageDigest. Required algorithm not present.", e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + @Override + public UploadDetails createDestinationAndUpload(String feedType, String contentType, File fileName) + throws FileNotFoundException, ApiException { + long documentLength = fileName.length(); + + return createDestinationAndUpload(feedType, contentType, documentLength, new FileInputStream(fileName)); + } + + @Override + public UploadDetails createDestinationAndUpload(String feedType, String contentType, long documentLength, + InputStream stream) throws ApiException { + + UploadDestination destination = createDestination(feedType, contentType, documentLength); + + return uploadToDestination(destination, contentType, documentLength, stream); + } + + public UploadDestination createDestination(String feedType, String contentType, long documentLength) + throws ApiException { + Preconditions.checkArgument(documentLength < (long) Integer.MAX_VALUE, + "Document length cannot exceed " + Integer.MAX_VALUE + " bytes." + ); + CreateUploadDestinationResponse response = uploadsApi.createUploadDestinationForFeed(feedType, contentType, + (int) documentLength + ); + return response.getPayload(); + } +/* + TODO + + public void submitFeed(UploadDetails uploadDetails) { + + CompleteDocumentUploadInput completeUploadInput = new CompleteDocumentUploadInput(); + completeUploadInput.setDocumentId(uploadDestinationId); + completeUploadInput.setObfuscatedCustomerId(uploadDestination.getObfuscatedCustomerId()); + completeUploadInput.setCompleteUploadToken(getUploadUrlOutput.getCompleteUploadToken()); + completeUploadInput.setSha256sum(sha256sum); + completeUploadInput.setContentMd5(contentMd5); + CompleteDocumentUploadSyncJobBuilder.build(completeUploadInput, metricsStrategy, client).runOnce(); + + } +*/ + + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/DemuxOutputStream.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/DemuxOutputStream.java new file mode 100644 index 0000000..6fe8a68 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/DemuxOutputStream.java @@ -0,0 +1,93 @@ +package com.amazon.spapi.sequencing.client.util; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@code OutputStream} wrapper class that forwards data written to this stream to an associated stream and allows + * switching of the associated stream on the fly. Associated stream can be {@code null} in which case all operations + * are, essentially, NOOP. + */ +public class DemuxOutputStream extends OutputStream { + + private OutputStream out; + + /** + * Creates a new {@code DemuxOutputStream} wrapper around given output stream. Accepts {@code null}. + * @param stream + * the output stream (can be {@code null}) + */ + public DemuxOutputStream(@Nullable OutputStream stream) { + out = stream; + } + + /** + * Binds new output stream. Originally bound output stream will be flushed and returned. Accepts {@code null}. + * @param stream + * the new input stream to bind (can be {@code null}) + * @return the previously bound input stream (can be {@code null}) + * @throws IOException + * if there was an error flushing the currently bound stream + */ + @Nullable + public OutputStream bind(@Nullable OutputStream stream) throws IOException { + OutputStream tmp = out; + out = stream; + if (tmp != null) { + tmp.flush(); + } + return tmp; + } + + /** + * {@inheritDoc} + */ + @Override + public void close() throws IOException { + if (out != null) { + out.close(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void flush() throws IOException { + if (out != null) { + out.flush(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void write(byte[] data, int offset, int len) throws IOException { + if (out != null) { + out.write(data, offset, len); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void write(byte[] data) throws IOException { + if (out != null) { + out.write(data); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void write(int data) throws IOException { + if (out != null) { + out.write(data); + } + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/TempInputStream.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/TempInputStream.java new file mode 100644 index 0000000..59310d8 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/client/util/TempInputStream.java @@ -0,0 +1,133 @@ +package com.amazon.spapi.sequencing.client.util; + +import lombok.extern.apachecommons.CommonsLog; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.*; + +/** + * An InputStream from a temporary file that deletes the file after it was completely read or closed. + */ +@CommonsLog +public class TempInputStream extends InputStream { + + private final FileInputStream fileStream; + private final File tempFile; + + /** + * Creates a new {@code TempInputStream} for a given file. + * @param file + * the file + * @throws FileNotFoundException + * if file doesn't exist + */ + public TempInputStream(File file) throws FileNotFoundException { + fileStream = new FileInputStream(file); + tempFile = file; + } + + /** + * File cleanup. + */ + private void cleanup() { + IOUtils.closeQuietly(fileStream); + + try { + FileUtils.forceDelete(tempFile); + } catch (FileNotFoundException e) { + // Ignore not found exceptions + return; + } catch (IOException e) { + log.warn("IOException when deleting TempInputStream file", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void close() throws IOException { + try { + fileStream.close(); + } finally { + cleanup(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int read() throws IOException { + try { + int read = fileStream.read(); + if (read == -1) { + cleanup(); + } + return read; + } catch (IOException e) { + cleanup(); + throw e; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + try { + int read = fileStream.read(bytes, off, len); + if (read == -1) { + cleanup(); + } + return read; + } catch (IOException e) { + cleanup(); + throw e; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int read(byte[] bytes) throws IOException { + try { + int read = fileStream.read(bytes); + if (read == -1) { + cleanup(); + } + return read; + } catch (IOException e) { + cleanup(); + throw e; + } + } + + /** + * {@inheritDoc} + */ + @Override + public int available() throws IOException { + return fileStream.available(); + } + + /** + * {@inheritDoc} + */ + @Override + public long skip(long n) throws IOException { + return fileStream.skip(n); + } + + /** + * {@inheritDoc} + */ + @Override + protected void finalize() throws IOException { + close(); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/AsymmetricCryptoProvider.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/AsymmetricCryptoProvider.java new file mode 100644 index 0000000..9e48b2e --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/AsymmetricCryptoProvider.java @@ -0,0 +1,195 @@ +package com.amazon.spapi.sequencing.crypto; + +import com.google.common.base.Preconditions; + +import javax.annotation.Nonnull; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.InvalidKeyException; +import java.security.SignatureException; + + +/** + * An Asymmetric verison of {@link CryptoProvider} that uses RSA. + * Encryption is performed using RSA with PKCS1Padding. + * Signing is performed using SHA256withRSA. + */ +public class AsymmetricCryptoProvider implements CryptoProvider { + private static final String REQUIRED_KEY_ALGORITHM = "RSA"; + private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; + private static final String ENCRYPTION_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public byte[] encrypt(@Nonnull byte[] plainText, @Nonnull EncryptionMaterials materials) + throws EncryptionException { + Preconditions.checkArgument(plainText != null, "plainText must be non-null."); + + Cipher cipher = getInitializedCipher(Cipher.ENCRYPT_MODE, materials); + return applyCipher(cipher, plainText); + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public byte[] decrypt(@Nonnull byte[] cipherText, @Nonnull EncryptionMaterials materials) + throws EncryptionException { + Preconditions.checkArgument(cipherText != null, "cipherText must be non-null."); + + Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, materials); + return applyCipher(cipher, cipherText); + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public byte[] sign(@Nonnull byte[] bytesToSign, @Nonnull EncryptionMaterials materials) + throws SignatureGenerationException { + Preconditions.checkArgument(bytesToSign != null, "bytesToSign must be non-null."); + + PrivateKey key = validatePrivateKey(materials); + + byte[] signatureBytes; + try { + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(key, new SecureRandom()); + signature.update(bytesToSign); + signatureBytes = signature.sign(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(String.format("NoSuchAlgorithmException for %s", SIGNATURE_ALGORITHM), e); + } catch (InvalidKeyException e) { + throw new SignatureGenerationException(e); + } catch (SignatureException e) { + throw new SignatureGenerationException(e); + } + + return signatureBytes; + } + + /** + * {@inheritDoc} + */ + @Override + public void validateSignature(@Nonnull byte[] signatureBytes, @Nonnull byte[] bytesToSign, + @Nonnull EncryptionMaterials materials) throws SignatureGenerationException, + SignatureValidationException { + Preconditions.checkArgument(signatureBytes != null, "signatureBytes must be non-null."); + Preconditions.checkArgument(bytesToSign != null, "bytesToSign must be non-null."); + + PublicKey key = validatePublicKey(materials); + + try { + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(key); + signature.update(bytesToSign); + if (!signature.verify(signatureBytes)) { + throw new SignatureValidationException("Signature mismatch. Possible tampering attempt."); + } + } catch (InvalidKeyException e) { + throw new SignatureGenerationException(e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(String.format("NoSuchAlgorithmException for %s", SIGNATURE_ALGORITHM), e); + } catch (SignatureException e) { + throw new SignatureGenerationException(e); + } + } + + /** + * Helper function to apply a cipher to some data and return the results. Performs exception mapping. + * @param cipher the Cipher to apply. + * @param text the text to apply the cipher to + * @return the text generated by applying the cipher to the input text + * @throws EncryptionException if there is a problem applying the cipher. + */ + @Nonnull + private byte[] applyCipher(@Nonnull Cipher cipher, @Nonnull byte[] text) throws EncryptionException { + try { + return cipher.doFinal(text); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException(e); + } catch (BadPaddingException e) { + throw new EncryptionException(e); + } + } + + /** + * {@inheritDoc} + */ + @Nonnull + public Cipher getInitializedCipher(int mode, @Nonnull EncryptionMaterials materials) throws EncryptionException { + + Key key; + if (Cipher.DECRYPT_MODE == mode) { + key = validatePrivateKey(materials); + } else { + key = validatePublicKey(materials); + } + + Cipher cipher; + try { + cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + cipher.init(mode, key, new SecureRandom()); + } catch (InvalidKeyException e) { + throw new EncryptionException(e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(String.format("NoSuchAlgorithmException for %s", SIGNATURE_ALGORITHM), e); + } catch (NoSuchPaddingException e) { + throw new IllegalStateException(String.format("NoSuchPaddingException for %s", SIGNATURE_ALGORITHM), e); + } + + return cipher; + } + + /** + * Helper function to ensure EncryptionMaterials are valid and the Key matches a specific class. + * @param materials the Materials to validate + * @param clazz the expected class of the key. + * @param message The error message if the key is not of the expected class + * @return the key. + */ + @Nonnull + private Key validateKey(@Nonnull EncryptionMaterials materials, @Nonnull Class clazz, + @Nonnull String message) { + Preconditions.checkArgument(materials != null, "Materials must be non-null."); + Preconditions.checkArgument(REQUIRED_KEY_ALGORITHM.equals(materials.getKey().getAlgorithm()), + "AsymmetricCryptoProvider requires an RSA key."); + Preconditions.checkArgument(clazz.isAssignableFrom(materials.getKey().getClass()), message); + + return materials.getKey(); + } + + /** + * Helper function that validates that the EncryptionMaterials contain a valid PrivateKey. + * @param materials the materials to validate + * @return the PrivateKey + */ + @Nonnull + private PrivateKey validatePrivateKey(@Nonnull EncryptionMaterials materials) { + return (PrivateKey) validateKey(materials, PrivateKey.class, "Key must be of type PrivateKey."); + } + + /** + * Helper function that validates that the EncryptionMaterials contain a valid PublicKey. + * @param materials the materials to validate + * @return the PublicKey + */ + @Nonnull + private PublicKey validatePublicKey(@Nonnull EncryptionMaterials materials) { + return (PublicKey) validateKey(materials, PublicKey.class, "Key must be of type PublicKey."); + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoException.java new file mode 100644 index 0000000..02dfed4 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoException.java @@ -0,0 +1,34 @@ +package com.amazon.spapi.sequencing.crypto; + + +/** + * Base class for any Cryptographic exception that occurs. + */ +public class CryptoException extends Exception { + + /** + * Constructor taking both a message and cause. + * @param message the message + * @param cause the cause + */ + public CryptoException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message the message + */ + public CryptoException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause the cause + */ + public CryptoException(Throwable cause) { + super(cause); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoProvider.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoProvider.java new file mode 100644 index 0000000..9d0a5a1 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/CryptoProvider.java @@ -0,0 +1,66 @@ +package com.amazon.spapi.sequencing.crypto; + +import javax.annotation.Nonnull; +import javax.crypto.Cipher; + +/** + * This class of objects performs encryption, decryption, signing (or hmacing), and signature/hmac validation. + */ +public interface CryptoProvider { + + /** + * Encrypt plain text into cipher text. + * @param plainText the plain text to encrypt. + * @param materials the EncryptionMaterials to use to perform the encryption + * @return the cipherText + * @throws EncryptionException if any problems occur during encryption. + */ + @Nonnull + byte[] encrypt(@Nonnull byte[] plainText, @Nonnull EncryptionMaterials materials) throws EncryptionException; + + /** + * Decrypts the cipher text into plain text. + * @param cipherText the cipher text to decrypt + * @param materials the EncryptionMaterials to use to perform decryption + * @return the plain text + * @throws EncryptionException if any problems occur during decryption. + */ + @Nonnull + byte[] decrypt(@Nonnull byte[] cipherText, @Nonnull EncryptionMaterials materials) throws EncryptionException; + + /** + * Create a signature or hmac based on the bytes provided. + * @param bytesToSign the bytes to use when calculating the signature or hmac + * @param materials the materials to use when generating the signature or hmac + * @return the signature/hmac bytes + * @throws SignatureGenerationException if any problems occur during signing/hmacing + */ + @Nonnull + byte[] sign(@Nonnull byte[] bytesToSign, @Nonnull EncryptionMaterials materials) + throws SignatureGenerationException; + + /** + * Validate that a signature/hmac matches the data that was signed. + * @param signature the signature/hmac to validate + * @param bytesToSign the data that was originally signed/hmac'd + * @param materials the materials to use when validating the signature or hmac + * @throws SignatureGenerationException if we are unable to generate the signature/hmac to validate against. + * @throws SignatureValidationException if the validation fails (i.e, the signature or hmac do not match). + * This indicates data corruption or a possible tampering attack. + */ + @Nonnull + void validateSignature(@Nonnull byte[] signature, @Nonnull byte[] bytesToSign, + @Nonnull EncryptionMaterials materials) throws SignatureGenerationException, + SignatureValidationException; + + /** + * Correctly Initialize a Cipher given the mode. The mode is specified by {@link Cipher}'s Mode constants. + * @param mode The mode of the cipher (i.e., encrypt or decrypt). See {@link Cipher}'s Mode constants. + * @param materials The EncryptionMaterials to initialize the cipher with + * @return an initialized Cipher object + * @throws EncryptionException if the Cipher could not be initialized. + */ + @Nonnull + Cipher getInitializedCipher(int mode, @Nonnull EncryptionMaterials materials) throws EncryptionException; + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/DefaultKeyConverter.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/DefaultKeyConverter.java new file mode 100644 index 0000000..893a783 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/DefaultKeyConverter.java @@ -0,0 +1,77 @@ +package com.amazon.spapi.sequencing.crypto; + +import com.google.common.base.Preconditions; + +import javax.annotation.Nonnull; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * Default implementation of the {@link KeyConverter} interface. + */ +public class DefaultKeyConverter implements KeyConverter { + private static final String SECRET_KEY_ALGORITHM = "AES"; + private static final String KEYPAIR_ALGORITHM = "RSA"; + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public SecretKey convertToSymmetricKey(@Nonnull byte[] material) { + Preconditions.checkArgument(material != null, "material must be non-null."); + return new SecretKeySpec(material, SECRET_KEY_ALGORITHM); + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public PrivateKey convertToPrivateKey(@Nonnull byte[] material) throws com.amazon.spapi.sequencing.crypto.InvalidKeyException { + Preconditions.checkArgument(material != null, "material must be non-null."); + + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(material); + try { + KeyFactory factory = KeyFactory.getInstance(KEYPAIR_ALGORITHM); + return factory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("RSA Algorithm is invalid. This indicates a badly configured Provider."); + } catch (InvalidKeySpecException e) { + throw new com.amazon.spapi.sequencing.crypto.InvalidKeyException("Invalid key.", e); + } + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public PublicKey convertToPublicKey(@Nonnull byte[] material) throws com.amazon.spapi.sequencing.crypto.InvalidKeyException { + Preconditions.checkArgument(material != null, "material must be non-null."); + + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(material); + try { + KeyFactory factory = KeyFactory.getInstance(KEYPAIR_ALGORITHM); + return factory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("RSA Algorithm is invalid. This indicates a badly configured Provider."); + } catch (InvalidKeySpecException e) { + throw new com.amazon.spapi.sequencing.crypto.InvalidKeyException("Invalid key.", e); + } + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public KeyPair convertToKeyPair(@Nonnull byte[] publicMaterial, @Nonnull byte[] privateMaterial) + throws InvalidKeyException { + return new KeyPair(convertToPublicKey(publicMaterial), convertToPrivateKey(privateMaterial)); + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionException.java new file mode 100644 index 0000000..14481ca --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionException.java @@ -0,0 +1,32 @@ +package com.amazon.spapi.sequencing.crypto; + +/** + * An exception that indicates an error occured during encryption or decryption of some data. + */ +public class EncryptionException extends CryptoException { + + /** + * Constructor taking both a message and cause. + * @param message the message + * @param cause the cause + */ + public EncryptionException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message the message + */ + public EncryptionException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause the cause + */ + public EncryptionException(Throwable cause) { + super(cause); + } +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionMaterials.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionMaterials.java new file mode 100644 index 0000000..057feb9 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/EncryptionMaterials.java @@ -0,0 +1,60 @@ +package com.amazon.spapi.sequencing.crypto; + +import com.google.common.base.Preconditions; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.security.Key; +import java.util.Arrays; + +/** + * Key and initialization vector for use when encrypting things. + */ +@EqualsAndHashCode +public class EncryptionMaterials { + + @Nonnull + @Getter + private final Key key; + @Nullable + private final byte[] initializationVector; + + /** + * All Args Constructor. + * @param key non null Key for this EncryptionMaterials + * @param initializationVector Nullable initializationVector (used for CBC mode encryption). + */ + public EncryptionMaterials(@Nonnull Key key, @Nullable byte[] initializationVector) { + Preconditions.checkArgument(key != null, "Key must be non-null."); + + this.key = key; + this.initializationVector = copyInitializationVector(initializationVector); + } + + /** + * Get a copy of the initialization vector bytes. + * @return A copy of the initialization vector bytes. + */ + @Nullable + public byte[] getInitializationVector() { + return copyInitializationVector(initializationVector); + } + + /** + * A null-safe helper that copies the initialization vector bytes into a new array if the IV is non-null. + * @param iv the byte array to copy + * @return a copy of iv if iv is non null, otherwise null. + */ + @Nullable + private byte[] copyInitializationVector(byte[] iv) { + byte[] newInitializationVector = null; + if (iv != null) { + newInitializationVector = Arrays.copyOf(iv, iv.length); + } + + return newInitializationVector; + } +} + diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/InvalidKeyException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/InvalidKeyException.java new file mode 100644 index 0000000..17c7610 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/InvalidKeyException.java @@ -0,0 +1,34 @@ +package com.amazon.spapi.sequencing.crypto; + +/** + * InvalidKeyException indicates that a key was invalid. + * + */ +public class InvalidKeyException extends CryptoException { + + /** + * Constructor taking both a message and cause. + * @param message the message + * @param cause the cause + */ + public InvalidKeyException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message the message + */ + public InvalidKeyException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause the cause + */ + public InvalidKeyException(Throwable cause) { + super(cause); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/KeyConverter.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/KeyConverter.java new file mode 100644 index 0000000..d6df5fc --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/KeyConverter.java @@ -0,0 +1,50 @@ +package com.amazon.spapi.sequencing.crypto; + +import javax.annotation.Nonnull; +import javax.crypto.SecretKey; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * A class of objects that converts the raw bytes of an encoded key into a Key. + */ +public interface KeyConverter { + + /** + * Convert a byte[] into a SecretKey. + * @param material the byte[] to covert + * @return the SecretKey + */ + @Nonnull + SecretKey convertToSymmetricKey(@Nonnull byte[] material); + + /** + * Convert a PKCS8 encoded byte[] into a PrivateKey. + * @param material the byte[] to covert + * @return the PrivateKey + * @throws InvalidKeyException if the byte[] is not a properly encoded key + */ + @Nonnull + PrivateKey convertToPrivateKey(@Nonnull byte[] material) throws InvalidKeyException; + + /** + * Convert a X509 encoded byte[] into a PublicKey. + * @param material the byte[] to covert + * @return the PublicKey + * @throws InvalidKeyException if the byte[] is not a properly encoded key + */ + @Nonnull + PublicKey convertToPublicKey(@Nonnull byte[] material) throws InvalidKeyException; + + /** + * Convert a X509 encoded byte[] and a PKCS8 encoded byte[] into a KeyPair. + * @param publicMaterial the X509 encoded byte[] to covert into the public key + * @param privateMaterial the PKCS8 encoded byte[] to covert into the private key + * @return the SecretKey + * @throws InvalidKeyException if either the public or private material is invalid + */ + @Nonnull + KeyPair convertToKeyPair(@Nonnull byte[] publicMaterial, @Nonnull byte[] privateMaterial) + throws InvalidKeyException; +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureException.java new file mode 100644 index 0000000..c121a20 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureException.java @@ -0,0 +1,33 @@ +package com.amazon.spapi.sequencing.crypto; + +/** + * Base class for exceptions related to Signature/HMAC problems. + */ +public class SignatureException extends CryptoException { + + /** + * Constructor taking both a message and cause. + * @param message the message + * @param cause the cause + */ + public SignatureException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message the message + */ + public SignatureException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause the cause + */ + public SignatureException(Throwable cause) { + super(cause); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureGenerationException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureGenerationException.java new file mode 100644 index 0000000..652f385 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureGenerationException.java @@ -0,0 +1,33 @@ +package com.amazon.spapi.sequencing.crypto; + +/** + * Indicates that an error occured when generating a Signature or HMAC. + */ +public class SignatureGenerationException extends SignatureException { + + /** + * Constructor taking both a message and cause. + * @param message the message + * @param cause the cause + */ + public SignatureGenerationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message the message + */ + public SignatureGenerationException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause the cause + */ + public SignatureGenerationException(Throwable cause) { + super(cause); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureValidationException.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureValidationException.java new file mode 100644 index 0000000..4474ab7 --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SignatureValidationException.java @@ -0,0 +1,34 @@ +package com.amazon.spapi.sequencing.crypto; + +/** + * Indicates that the signature/hmac did not verify and that there was a mismatch. + * This indicates either data corruption or a possible tamper attack. + */ +public class SignatureValidationException extends SignatureException { + + /** + * Constructor taking both a message and cause. + * @param message the message + * @param cause the cause + */ + public SignatureValidationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor taking only a message. + * @param message the message + */ + public SignatureValidationException(String message) { + super(message); + } + + /** + * Constructor taking only a cause. + * @param cause the cause + */ + public SignatureValidationException(Throwable cause) { + super(cause); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SymmetricCryptoProvider.java b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SymmetricCryptoProvider.java new file mode 100644 index 0000000..bec05af --- /dev/null +++ b/clients/sellingpartner-api-frp-helper-java/src/main/java/com/amazon/spapi/sequencing/crypto/SymmetricCryptoProvider.java @@ -0,0 +1,158 @@ +package com.amazon.spapi.sequencing.crypto; + +import com.google.common.base.Preconditions; +import lombok.extern.apachecommons.CommonsLog; + +import javax.annotation.Nonnull; +import javax.crypto.Mac; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.InvalidKeyException; +import java.util.Arrays; +/** + * A Symmetric verison of {@link CryptoProvider} that uses AES and HmacSha256. + * Encryption is performed using AES/CBC/PKCS5Padding + * HMAC is performed using HmacSha256. + */ +@CommonsLog +public class SymmetricCryptoProvider implements CryptoProvider { + private static final String REQUIRED_KEY_ALGORITHM = "AES"; + private static final String ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding"; + private static final String MAC_ALGORITHM = "HmacSha256"; + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public byte[] encrypt(@Nonnull byte[] plainText, @Nonnull EncryptionMaterials materials) + throws EncryptionException { + Preconditions.checkArgument(plainText != null, "plainText must be non-null."); + + Cipher cipher = getInitializedCipher(Cipher.ENCRYPT_MODE, materials); + + return applyCipher(cipher, plainText); + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public byte[] decrypt(@Nonnull byte[] cipherText, @Nonnull EncryptionMaterials materials) + throws EncryptionException { + Preconditions.checkArgument(cipherText != null, "cipherText must be non-null."); + + Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, materials); + + return applyCipher(cipher, cipherText); + } + + /** + * {@inheritDoc} + */ + @Nonnull + @Override + public byte[] sign(@Nonnull byte[] bytesToSign, @Nonnull EncryptionMaterials materials) + throws SignatureGenerationException { + Preconditions.checkArgument(bytesToSign != null, "bytesToSign must be non-null.."); + validateMaterials(materials, false); + + Mac mac; + try { + mac = Mac.getInstance(MAC_ALGORITHM); + mac.init(materials.getKey()); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(String.format("NoSuchAlgorithmException for %s", MAC_ALGORITHM), e); + } catch (InvalidKeyException e) { + throw new SignatureGenerationException(e); + } + + return mac.doFinal(bytesToSign); + } + + /** + * {@inheritDoc} + */ + @Override + public void validateSignature(@Nonnull byte[] signature, @Nonnull byte[] bytesToSign, + @Nonnull EncryptionMaterials materials) throws SignatureValidationException, SignatureGenerationException { + Preconditions.checkArgument(signature != null, "signature must be non-null."); + Preconditions.checkArgument(bytesToSign != null, "bytesToSign must be non-null."); + validateMaterials(materials, false); + + byte[] expectedSignature = sign(bytesToSign, materials); + + if (!MessageDigest.isEqual(signature, expectedSignature)) { + if (log.isTraceEnabled()) { + log.trace(String.format("Signature mismatch detected. Expected=%s, Actual=%s", + Arrays.toString(expectedSignature), Arrays.toString(signature))); + } + throw new SignatureValidationException("Signature mismatch. Possible tampering attempt."); + } + } + + /** + * Helper function to apply a cipher to some data and return the results. Performs exception mapping. + * @param cipher the Cipher to apply. + * @param text the text to apply the cipher to + * @return the text generated by applying the cipher to the input text + * @throws EncryptionException if there is a problem applying the cipher. + */ + @Nonnull + private byte[] applyCipher(@Nonnull Cipher cipher, @Nonnull byte[] text) throws EncryptionException { + try { + return cipher.doFinal(text); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException(e); + } catch (BadPaddingException e) { + throw new EncryptionException(e); + } + } + + /** + * {@inheritDoc} + */ + @Nonnull + public Cipher getInitializedCipher(int mode, @Nonnull EncryptionMaterials materials) throws EncryptionException { + validateMaterials(materials, true); + + Cipher cipher; + try { + cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); + cipher.init(mode, materials.getKey(), + new IvParameterSpec(materials.getInitializationVector()), new SecureRandom()); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(String.format("NoSuchAlgorithmException for %s", ENCRYPTION_ALGORITHM), e); + } catch (NoSuchPaddingException e) { + throw new IllegalStateException(String.format("NoSuchPaddingException for %s", ENCRYPTION_ALGORITHM), e); + } catch (InvalidKeyException e) { + throw new EncryptionException(e); + } catch (InvalidAlgorithmParameterException e) { + throw new EncryptionException(e); + } + + return cipher; + } + + /** + * Helper function to ensure EncryptionMaterials are valid. + * @param materials the Materials to validate + * @param ivRequired whether or not this function should treat the InitializationVector as required. + * The IV is required for encryption/decryption but not for HMAC operations. + */ + private void validateMaterials(@Nonnull EncryptionMaterials materials, boolean ivRequired) { + Preconditions.checkArgument(materials != null, "Materials must be non-null."); + Preconditions.checkArgument(REQUIRED_KEY_ALGORITHM.equals(materials.getKey().getAlgorithm()), + "SymmetricCryptoProvider requires an AES Key."); + Preconditions.checkArgument(!ivRequired || materials.getInitializationVector() != null, "IV must be non-null."); + } + +} diff --git a/clients/sellingpartner-api-frp-helper-java/src/main/resources/certs/InternalAndExternalTrustStore.jks b/clients/sellingpartner-api-frp-helper-java/src/main/resources/certs/InternalAndExternalTrustStore.jks new file mode 100644 index 0000000000000000000000000000000000000000..f2d9548dd7df05213baacf0fdb0b043cf4facbdb GIT binary patch literal 106525 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±nJv$)0&QjFj7FIM%bW;yobCk-&Di;k& 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�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 z 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 \ No newline at end of file