Java Auth and auth lib changes

This commit is contained in:
Meena Peri 2023-09-29 17:37:57 -05:00
parent 39dd71cdb6
commit fcf32fdff6
19 changed files with 241 additions and 862 deletions

View File

@ -38,39 +38,6 @@ com.squareup.okhttp.Request signedRequest = new LWAAuthorizationSigner(lwaAuthor
.sign(request); .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);
/*Signs request using IAM role credentials.
AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider = AWSAuthenticationCredentialsProvider.builder()
.roleArn("...")
.roleSessionName("...")
.build();
com.squareup.okhttp.Request signedRequest = new
AWSSigV4Signer(awsAuthenticationCredentialsProvider.getCredentials())
.sign(request);
```
## LWAAccessTokenCache ## LWAAccessTokenCache
Interface to implement cache for access token that is returned in LWAClient and reuse the access token until time to live. Interface to implement cache for access token that is returned in LWAClient and reuse the access token until time to live.
@ -79,7 +46,6 @@ Interface to set and get rateLimit configurations that are used with RateLimiter
*Example* *Example*
``` ```
com.squareup.okhttp.Request request = new Request.Builder() com.squareup.okhttp.Request request = new Request.Builder()
.url(...) .url(...)
... ...
@ -89,8 +55,22 @@ com.squareup.okhttp.Request request = new Request.Builder()
.rateLimitPermit(...) .rateLimitPermit(...)
.waitTimeOutInMilliSeconds(...) .waitTimeOutInMilliSeconds(...)
.build(); .build();
``` ```
## Exception
This package returns a custom LWAException when there is an error returned during LWA authorization. LWAException provides additional details like errorCode and errorDescription to help fix the issue.
*Example*
```
catch (LWAException e) {
System.err.println("LWA Exception when calling Selling partner API");
System.err.println(e.getErrorCode());
System.err.println(e.getErrorMessage());
e.printStackTrace();
}
```
## Version
Selling Partner API Authentication/Authorization Library version 2.0.
## Resources ## Resources
This package features Mustache templates designed for use with [swagger codegen](https://swagger.io/tools/swagger-codegen/). This package features Mustache templates designed for use with [swagger codegen](https://swagger.io/tools/swagger-codegen/).

View File

@ -6,7 +6,7 @@
<groupId>com.amazon.sellingpartnerapi</groupId> <groupId>com.amazon.sellingpartnerapi</groupId>
<artifactId>sellingpartnerapi-aa-java</artifactId> <artifactId>sellingpartnerapi-aa-java</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.0</version> <version>2.0</version>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -0,0 +1,3 @@
{
"artifactVersion": "2.0"
}

View File

@ -51,10 +51,10 @@ import {{invokerPackage}}.auth.HttpBasicAuth;
import {{invokerPackage}}.auth.ApiKeyAuth; import {{invokerPackage}}.auth.ApiKeyAuth;
import {{invokerPackage}}.auth.OAuth; import {{invokerPackage}}.auth.OAuth;
import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer;
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner; import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner;
import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.RateLimiter;
import com.amazon.SellingPartnerAPIAA.RateLimitConfiguration; import com.amazon.SellingPartnerAPIAA.RateLimitConfiguration;
import com.amazon.SellingPartnerAPIAA.LWAException;
public class ApiClient { public class ApiClient {
@ -79,7 +79,6 @@ public class ApiClient {
private HttpLoggingInterceptor loggingInterceptor; private HttpLoggingInterceptor loggingInterceptor;
private LWAAuthorizationSigner lwaAuthorizationSigner; private LWAAuthorizationSigner lwaAuthorizationSigner;
private AWSSigV4Signer awsSigV4Signer;
private RateLimiter rateLimiter; private RateLimiter rateLimiter;
private RateLimitConfiguration rateLimitConfiguration; private RateLimitConfiguration rateLimitConfiguration;
@ -496,17 +495,6 @@ public class ApiClient {
this.lwaAuthorizationSigner = lwaAuthorizationSigner; this.lwaAuthorizationSigner = lwaAuthorizationSigner;
return this; return this;
} }
/**
* Sets the AWSSigV4Signer
*
* @param awsSigV4Signer AWSSigV4Signer instance
* @return Api client
*/
public ApiClient setAWSSigV4Signer(AWSSigV4Signer awsSigV4Signer) {
this.awsSigV4Signer = awsSigV4Signer;
return this;
}
/** /**
* Sets the RateLimiter * Sets the RateLimiter
@ -985,8 +973,9 @@ public class ApiClient {
* @param progressRequestListener Progress request listener * @param progressRequestListener Progress request listener
* @return The HTTP call * @return The HTTP call
* @throws ApiException If fail to serialize the request body object * @throws ApiException If fail to serialize the request body object
* @throws LWAException If calls to fetch LWA access token fails
*/ */
public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException {
Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener); Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener);
return httpClient.newCall(request); return httpClient.newCall(request);
@ -1006,8 +995,9 @@ public class ApiClient {
* @param progressRequestListener Progress request listener * @param progressRequestListener Progress request listener
* @return The HTTP request * @return The HTTP request
* @throws ApiException If fail to serialize the request body object * @throws ApiException If fail to serialize the request body object
* @throws LWAException If calls to fetch LWA access token fails
*/ */
public Request buildRequest(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { public Request buildRequest(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException {
updateParamsForAuth(authNames, queryParams, headerParams); updateParamsForAuth(authNames, queryParams, headerParams);
final String url = buildUrl(path, queryParams, collectionQueryParams); final String url = buildUrl(path, queryParams, collectionQueryParams);
@ -1049,7 +1039,6 @@ public class ApiClient {
} }
request = lwaAuthorizationSigner.sign(request); request = lwaAuthorizationSigner.sign(request);
request = awsSigV4Signer.sign(request);
return request; return request;
} }

View File

@ -43,15 +43,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
{{/fullJavaUtil}} {{/fullJavaUtil}}
import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentials;
import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentialsProvider;
import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCustomCredentialsProvider;
import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer;
import com.amazon.SellingPartnerAPIAA.LWAAccessTokenCache; import com.amazon.SellingPartnerAPIAA.LWAAccessTokenCache;
import com.amazon.SellingPartnerAPIAA.LWAAccessTokenCacheImpl; import com.amazon.SellingPartnerAPIAA.LWAAccessTokenCacheImpl;
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials; import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials;
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner; import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner;
import com.amazon.SellingPartnerAPIAA.RateLimitConfiguration; import com.amazon.SellingPartnerAPIAA.RateLimitConfiguration;
import com.amazon.SellingPartnerAPIAA.LWAException;
{{#operations}} {{#operations}}
public class {{classname}} { public class {{classname}} {
@ -81,6 +78,7 @@ public class {{classname}} {
* @param progressRequestListener Progress request listener * @param progressRequestListener Progress request listener
* @return Call to execute * @return Call to execute
* @throws ApiException If fail to serialize the request body object * @throws ApiException If fail to serialize the request body object
* @throws LWAException If calls to fetch LWA access token fails
{{#isDeprecated}} {{#isDeprecated}}
* @deprecated * @deprecated
{{/isDeprecated}} {{/isDeprecated}}
@ -92,7 +90,7 @@ public class {{classname}} {
{{#isDeprecated}} {{#isDeprecated}}
@Deprecated @Deprecated
{{/isDeprecated}} {{/isDeprecated}}
public com.squareup.okhttp.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { public com.squareup.okhttp.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException {
Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
// create path and map variables // create path and map variables
@ -144,7 +142,7 @@ public class {{classname}} {
@Deprecated @Deprecated
{{/isDeprecated}} {{/isDeprecated}}
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private com.squareup.okhttp.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { private com.squareup.okhttp.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException {
{{^performBeanValidation}} {{^performBeanValidation}}
{{#allParams}}{{#required}} {{#allParams}}{{#required}}
// verify the required parameter '{{paramName}}' is set // verify the required parameter '{{paramName}}' is set
@ -191,6 +189,7 @@ public class {{classname}} {
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#returnType}} * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#returnType}}
* @return {{returnType}}{{/returnType}} * @return {{returnType}}{{/returnType}}
* @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
* @throws LWAException If calls to fetch LWA access token fails
{{#isDeprecated}} {{#isDeprecated}}
* @deprecated * @deprecated
{{/isDeprecated}} {{/isDeprecated}}
@ -202,7 +201,7 @@ public class {{classname}} {
{{#isDeprecated}} {{#isDeprecated}}
@Deprecated @Deprecated
{{/isDeprecated}} {{/isDeprecated}}
public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException,LWAException {
{{#returnType}}ApiResponse<{{{returnType}}}> {{localVariablePrefix}}resp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} {{#returnType}}ApiResponse<{{{returnType}}}> {{localVariablePrefix}}resp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}}
return {{localVariablePrefix}}resp.getData();{{/returnType}} return {{localVariablePrefix}}resp.getData();{{/returnType}}
} }
@ -213,6 +212,7 @@ public class {{classname}} {
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}
* @return ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Void{{/returnType}}&gt; * @return ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Void{{/returnType}}&gt;
* @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body
* @throws LWAException If calls to fetch LWA access token fails
{{#isDeprecated}} {{#isDeprecated}}
* @deprecated * @deprecated
{{/isDeprecated}} {{/isDeprecated}}
@ -224,7 +224,7 @@ public class {{classname}} {
{{#isDeprecated}} {{#isDeprecated}}
@Deprecated @Deprecated
{{/isDeprecated}} {{/isDeprecated}}
public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException,LWAException {
com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null, null); com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null, null);
{{#returnType}}Type {{localVariablePrefix}}localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType(); {{#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}} return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call, {{localVariablePrefix}}localVarReturnType);{{/returnType}}{{^returnType}}return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call);{{/returnType}}
@ -237,6 +237,7 @@ public class {{classname}} {
* @param callback The callback to be executed when the API call finishes * @param callback The callback to be executed when the API call finishes
* @return The request call * @return The request call
* @throws ApiException If fail to process the API call, e.g. serializing the request body object * @throws ApiException If fail to process the API call, e.g. serializing the request body object
* @throws LWAException If calls to fetch LWA access token fails
{{#isDeprecated}} {{#isDeprecated}}
* @deprecated * @deprecated
{{/isDeprecated}} {{/isDeprecated}}
@ -248,7 +249,7 @@ public class {{classname}} {
{{#isDeprecated}} {{#isDeprecated}}
@Deprecated @Deprecated
{{/isDeprecated}} {{/isDeprecated}}
public com.squareup.okhttp.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException { public com.squareup.okhttp.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException, LWAException {
ProgressResponseBody.ProgressListener progressListener = null; ProgressResponseBody.ProgressListener progressListener = null;
ProgressRequestBody.ProgressRequestListener progressRequestListener = null; ProgressRequestBody.ProgressRequestListener progressRequestListener = null;
@ -277,19 +278,12 @@ public class {{classname}} {
{{/operation}} {{/operation}}
public static class Builder { public static class Builder {
private AWSAuthenticationCredentials awsAuthenticationCredentials;
private LWAAuthorizationCredentials lwaAuthorizationCredentials; private LWAAuthorizationCredentials lwaAuthorizationCredentials;
private String endpoint; private String endpoint;
private LWAAccessTokenCache lwaAccessTokenCache; private LWAAccessTokenCache lwaAccessTokenCache;
private Boolean disableAccessTokenCache = false; private Boolean disableAccessTokenCache = false;
private AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider;
private RateLimitConfiguration rateLimitConfiguration; private RateLimitConfiguration rateLimitConfiguration;
private AWSAuthenticationCustomCredentialsProvider awsAuthenticationCustomCredentialsProvider;
public Builder awsAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) {
this.awsAuthenticationCredentials = awsAuthenticationCredentials;
return this;
}
public Builder lwaAuthorizationCredentials(LWAAuthorizationCredentials lwaAuthorizationCredentials) { public Builder lwaAuthorizationCredentials(LWAAuthorizationCredentials lwaAuthorizationCredentials) {
this.lwaAuthorizationCredentials = lwaAuthorizationCredentials; this.lwaAuthorizationCredentials = lwaAuthorizationCredentials;
@ -310,12 +304,7 @@ public class {{classname}} {
this.disableAccessTokenCache = true; this.disableAccessTokenCache = true;
return this; return this;
} }
public Builder awsAuthenticationCredentialsProvider(AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) {
this.awsAuthenticationCredentialsProvider = awsAuthenticationCredentialsProvider;
return this;
}
public Builder rateLimitConfigurationOnRequests(RateLimitConfiguration rateLimitConfiguration){ public Builder rateLimitConfigurationOnRequests(RateLimitConfiguration rateLimitConfiguration){
this.rateLimitConfiguration = rateLimitConfiguration; this.rateLimitConfiguration = rateLimitConfiguration;
return this; return this;
@ -326,17 +315,7 @@ public class {{classname}} {
return this; return this;
} }
public Builder awsAuthenticationCustomCredentialsProvider(AWSAuthenticationCustomCredentialsProvider awsAuthenticationCustomCredentialsProvider) {
this.awsAuthenticationCustomCredentialsProvider = awsAuthenticationCustomCredentialsProvider;
return this;
}
public {{classname}} build() { public {{classname}} build() {
if (awsAuthenticationCredentials == null && awsAuthenticationCustomCredentialsProvider == null) {
throw new RuntimeException("Neither AWSAuthenticationCredentials or AWSAuthenticationCustomCredentialsProvider are set");
}
if (lwaAuthorizationCredentials == null) { if (lwaAuthorizationCredentials == null) {
throw new RuntimeException("LWAAuthorizationCredentials not set"); throw new RuntimeException("LWAAuthorizationCredentials not set");
} }
@ -344,17 +323,6 @@ public class {{classname}} {
if (StringUtil.isEmpty(endpoint)) { if (StringUtil.isEmpty(endpoint)) {
throw new RuntimeException("Endpoint not set"); throw new RuntimeException("Endpoint not set");
} }
AWSSigV4Signer awsSigV4Signer;
if (awsAuthenticationCustomCredentialsProvider != null ) {
awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCustomCredentialsProvider);
}
else if (awsAuthenticationCredentialsProvider == null) {
awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials);
}
else {
awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials,awsAuthenticationCredentialsProvider);
}
LWAAuthorizationSigner lwaAuthorizationSigner = null; LWAAuthorizationSigner lwaAuthorizationSigner = null;
if (disableAccessTokenCache) { if (disableAccessTokenCache) {
@ -368,7 +336,6 @@ public class {{classname}} {
} }
return new {{classname}}(new ApiClient() return new {{classname}}(new ApiClient()
.setAWSSigV4Signer(awsSigV4Signer)
.setLWAAuthorizationSigner(lwaAuthorizationSigner) .setLWAAuthorizationSigner(lwaAuthorizationSigner)
.setBasePath(endpoint) .setBasePath(endpoint)
.setRateLimiter(rateLimitConfiguration)); .setRateLimiter(rateLimitConfiguration));

View File

@ -0,0 +1,46 @@
{{>licenseInfo}}
package {{package}};
import {{invokerPackage}}.ApiException;
{{#imports}}import {{import}};
{{/imports}}
import org.junit.Test;
import org.junit.Ignore;
{{^fullJavaUtil}}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
{{/fullJavaUtil}}
import com.amazon.SellingPartnerAPIAA.LWAException;
/**
* API tests for {{classname}}
*/
@Ignore
public class {{classname}}Test {
private final {{classname}} api = new {{classname}}();
{{#operations}}{{#operation}}
/**
* {{summary}}
*
* {{notes}}
*
* @throws ApiException if the Api call fails
* @throws LWAException If calls to fetch LWA access token fails
*/
@Test
public void {{operationId}}Test() throws ApiException, LWAException {
{{#allParams}}
{{{dataType}}} {{paramName}} = null;
{{/allParams}}
{{#returnType}}{{{returnType}}} response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
// TODO: test validations
}
{{/operation}}{{/operations}}
}

View File

@ -1,30 +0,0 @@
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;
}

View File

@ -1,23 +0,0 @@
package com.amazon.SellingPartnerAPIAA;
import lombok.Builder;
import lombok.Data;
/**
* AWSAuthenticationCredentialsProvider
*/
@Data
@Builder
public class AWSAuthenticationCredentialsProvider {
/**
* AWS IAM Role ARN
*/
private String roleArn;
/**
* AWS IAM Role Session Name
*/
private String roleSessionName;
private String region;
}

View File

@ -1,23 +0,0 @@
package com.amazon.SellingPartnerAPIAA;
import lombok.Builder;
import lombok.Data;
import com.amazonaws.auth.AWSCredentialsProvider;
/**
* AWSAuthenticationCustomCredentialsProvider
*/
@Data
@Builder
public class AWSAuthenticationCustomCredentialsProvider {
/**
* AWS Region
*/
private String region;
/**
* AWS Credentials Provider
*/
private AWSCredentialsProvider awsCredentialsProvider;
}

View File

@ -1,109 +0,0 @@
package com.amazon.SellingPartnerAPIAA;
import com.amazonaws.SignableRequest;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.squareup.okhttp.Request;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
/**
* 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;
@Setter(AccessLevel.PACKAGE)
@Getter(AccessLevel.PACKAGE)
private AWSCredentialsProvider awsCredentialsProvider;
/**
*
* @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());
}
/**
*
* @param awsAuthenticationCredentials and awsAuthenticationCredentialsProvider AWS Developer Account Credentials
*/
public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials,
AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) {
aws4Signer = new AWS4Signer();
aws4Signer.setServiceName(SERVICE_NAME);
final String region;
AWSSecurityTokenServiceClientBuilder stsClientBuilder = AWSSecurityTokenServiceClientBuilder.standard();
if (awsAuthenticationCredentials != null) {
region = awsAuthenticationCredentials.getRegion();
BasicAWSCredentials awsBasicCredentials = new BasicAWSCredentials(
awsAuthenticationCredentials.getAccessKeyId(),
awsAuthenticationCredentials.getSecretKey()
);
stsClientBuilder.withCredentials(new AWSStaticCredentialsProvider(awsBasicCredentials));
} else {
region = awsAuthenticationCredentialsProvider.getRegion();
}
aws4Signer.setRegionName(region);
awsCredentialsProvider = new STSAssumeRoleSessionCredentialsProvider.Builder(
awsAuthenticationCredentialsProvider.getRoleArn(),
awsAuthenticationCredentialsProvider.getRoleSessionName())
.withStsClient(stsClientBuilder.withRegion(region).build())
.build();
}
/**
*
* @param awsAuthenticationCustomCredentialsProvider AWS Credentials Provider
*/
public AWSSigV4Signer(AWSAuthenticationCustomCredentialsProvider awsAuthenticationCustomCredentialsProvider) {
aws4Signer = new AWS4Signer();
aws4Signer.setServiceName(SERVICE_NAME);
aws4Signer.setRegionName(awsAuthenticationCustomCredentialsProvider.getRegion());
this.awsCredentialsProvider = awsAuthenticationCustomCredentialsProvider.getAwsCredentialsProvider();
}
/**
*
* @param awsAuthenticationCredentialsProvider AWS Credentials Provider containing the role name to be assumed
*/
public AWSSigV4Signer(AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) {
this(null, awsAuthenticationCredentialsProvider);
}
/**
* 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<Request> signableRequest = new SignableRequestImpl(originalRequest);
if (awsCredentialsProvider != null) {
aws4Signer.sign(signableRequest, awsCredentialsProvider.getCredentials());
} else {
aws4Signer.sign(signableRequest, awsCredentials);
}
return (Request) signableRequest.getOriginalRequestObject();
}
}

View File

@ -64,8 +64,9 @@ public class LWAAuthorizationSigner {
* Signs a Request with an LWA Access Token * Signs a Request with an LWA Access Token
* @param originalRequest Request to sign (treated as immutable) * @param originalRequest Request to sign (treated as immutable)
* @return Copy of originalRequest with LWA signature * @return Copy of originalRequest with LWA signature
* @throws LWAException If calls to fetch LWA access token fails
*/ */
public Request sign(Request originalRequest) { public Request sign(Request originalRequest) throws LWAException {
String accessToken = lwaClient.getAccessToken(lwaAccessTokenRequestMeta); String accessToken = lwaClient.getAccessToken(lwaAccessTokenRequestMeta);
return originalRequest.newBuilder() return originalRequest.newBuilder()

View File

@ -11,8 +11,7 @@ import com.squareup.okhttp.Response;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.lang3.EnumUtils;
import java.io.IOException;
class LWAClient { class LWAClient {
private static final String ACCESS_TOKEN_KEY = "access_token"; private static final String ACCESS_TOKEN_KEY = "access_token";
@ -25,7 +24,9 @@ class LWAClient {
private OkHttpClient okHttpClient; private OkHttpClient okHttpClient;
private LWAAccessTokenCache lwaAccessTokenCache; private LWAAccessTokenCache lwaAccessTokenCache;
/** Sets cache to store access token until token is expired */ /**
* Sets cache to store access token until token is expired
*/
public void setLWAAccessTokenCache(LWAAccessTokenCache tokenCache) { public void setLWAAccessTokenCache(LWAAccessTokenCache tokenCache) {
this.lwaAccessTokenCache = tokenCache; this.lwaAccessTokenCache = tokenCache;
} }
@ -33,46 +34,54 @@ class LWAClient {
LWAClient(String endpoint) { LWAClient(String endpoint) {
okHttpClient = new OkHttpClient(); okHttpClient = new OkHttpClient();
this.endpoint = endpoint; this.endpoint = endpoint;
} }
String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) throws LWAException {
if (lwaAccessTokenCache != null) { if (lwaAccessTokenCache != null) {
return getAccessTokenFromCache(lwaAccessTokenRequestMeta); return getAccessTokenFromCache(lwaAccessTokenRequestMeta);
} else { } else {
return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta); return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta);
} }
} }
String getAccessTokenFromCache(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { String getAccessTokenFromCache(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) throws LWAException {
String accessTokenCacheData = (String) lwaAccessTokenCache.get(lwaAccessTokenRequestMeta); String accessTokenCacheData = (String) lwaAccessTokenCache.get(lwaAccessTokenRequestMeta);
if (accessTokenCacheData != null) { if (accessTokenCacheData != null) {
return accessTokenCacheData; return accessTokenCacheData;
} else { } else {
return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta); return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta);
} }
} }
String getAccessTokenFromEndpoint(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { String getAccessTokenFromEndpoint(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) throws LWAException {
RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, new Gson().toJson(lwaAccessTokenRequestMeta)); RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, new Gson().toJson(lwaAccessTokenRequestMeta));
Request accessTokenRequest = new Request.Builder().url(endpoint).post(requestBody).build(); Request accessTokenRequest = new Request.Builder().url(endpoint).post(requestBody).build();
LWAExceptionErrorCode lwaErrorCode = null;
String accessToken; String accessToken;
try { try {
Response response = okHttpClient.newCall(accessTokenRequest).execute(); 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(); JsonObject responseJson = new JsonParser().parse(response.body().string()).getAsJsonObject();
if (!response.isSuccessful()) {
// Check if response has element error and is a known LWA error code
if (responseJson.has("error") &&
EnumUtils.isValidEnum(LWAExceptionErrorCode.class, responseJson.get("error").getAsString())) {
throw new LWAException(responseJson.get("error").getAsString(),
responseJson.get("error_description").getAsString(), "Error getting LWA Token");
} else { // else throw other LWA error
throw new LWAException(LWAExceptionErrorCode.other.toString(), "Other LWA Exception",
"Error getting LWA Token");
}
}
accessToken = responseJson.get(ACCESS_TOKEN_KEY).getAsString(); accessToken = responseJson.get(ACCESS_TOKEN_KEY).getAsString();
if (lwaAccessTokenCache != null) { if (lwaAccessTokenCache != null) {
long timeToTokenexpiry = responseJson.get(ACCESS_TOKEN_EXPIRES_IN).getAsLong(); long timeToTokenexpiry = responseJson.get(ACCESS_TOKEN_EXPIRES_IN).getAsLong();
lwaAccessTokenCache.put(lwaAccessTokenRequestMeta, accessToken, timeToTokenexpiry); lwaAccessTokenCache.put(lwaAccessTokenRequestMeta, accessToken, timeToTokenexpiry);
}
} catch (Exception e) {
throw new RuntimeException("Error getting LWA Access Token", e);
} }
return accessToken; } catch (LWAException e) { // throw LWA exception
throw new LWAException(e.getErrorCode(), e.getErrorMessage(), e.getMessage());
} catch (Exception e) { // throw other runtime exceptions
throw new RuntimeException("Error getting LWA Token");
} }
return accessToken;
}
} }

View File

@ -0,0 +1,45 @@
package com.amazon.SellingPartnerAPIAA;
public class LWAException extends Exception {
private String errorMessage;
private String errorCode;
LWAException() {
super();
}
LWAException(String errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
LWAException(String errorCode, String errorMessage, String message) {
super(message);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
LWAException(String errorCode, String errorMessage, Throwable cause) {
super(cause);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
LWAException(String errorCode, String errorMessage, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public String getErrorCode() {
return this.errorCode;
}
public String getErrorMessage() {
return this.errorMessage;
}
}

View File

@ -0,0 +1,13 @@
package com.amazon.SellingPartnerAPIAA;
public enum LWAExceptionErrorCode {
access_denied,
invalid_grant,
invalid_request,
invalid_scope,
server_error,
temporarily_unavailable,
unauthorized_client,
invalid_client,
other;
}

View File

@ -1,148 +0,0 @@
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<Request> {
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<String, String> getHeaders() {
Map<String, String> 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() {
return originalRequest.url()
.getPath();
}
@Override
public Map<String, List<String>> getParameters() {
Map<String, List<String>> parameters = new HashMap<>();
try {
List<NameValuePair> 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();
}
}

View File

@ -1,148 +0,0 @@
package com.amazon.SellingPartnerAPIAA;
import com.amazonaws.SignableRequest;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
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.Mockito;
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";
private static final String TEST_ROLE_ARN = "arnrole";
private static final String TEST_ROLESESSION_NAME = "test";
@Mock
private AWS4Signer mockAWS4Signer;
@Mock
private AWSCredentialsProvider mockAWSCredentialsProvider;
@Mock
private AWSCredentials mockAWSCredentials;
private AWSSigV4Signer underTest;
private AWSSigV4Signer underTestCredentialsProvider;
@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<AWSCredentials> 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<SignableRequest> 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());
}
@Test
public void returnSignedRequestWithCredentialProvider() {
ArgumentCaptor<SignableRequest> signableRequestArgumentCaptor = ArgumentCaptor.forClass(SignableRequest.class);
Mockito.when(mockAWSCredentialsProvider.getCredentials()).thenReturn(mockAWSCredentials);
underTestCredentialsProvider = new AWSSigV4Signer(AWSAuthenticationCredentials.builder()
.accessKeyId(TEST_ACCESS_KEY_ID)
.secretKey(TEST_SECRET_KEY)
.region(TEST_REGION)
.build(), AWSAuthenticationCredentialsProvider.builder()
.roleArn(TEST_ROLE_ARN)
.roleSessionName(TEST_ROLESESSION_NAME)
.build()
);
underTestCredentialsProvider.setAws4Signer(mockAWS4Signer);
underTestCredentialsProvider.setAwsCredentialsProvider(mockAWSCredentialsProvider);
Request actualSignedRequest = underTestCredentialsProvider.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());
}
@Test
public void returnSignedRequestWithCustomCredentialsProvider() {
ArgumentCaptor<SignableRequest> signableRequestArgumentCaptor = ArgumentCaptor.forClass(SignableRequest.class);
Mockito.when(mockAWSCredentialsProvider.getCredentials()).thenReturn(mockAWSCredentials);
underTestCredentialsProvider = new AWSSigV4Signer(AWSAuthenticationCustomCredentialsProvider.builder()
.awsCredentialsProvider(mockAWSCredentialsProvider)
.region(TEST_REGION)
.build());
underTestCredentialsProvider.setAws4Signer(mockAWS4Signer);
Request actualSignedRequest = underTestCredentialsProvider.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());
}
}

View File

@ -98,7 +98,7 @@ public class LWAAuthorizationSignerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaAuthSigner") @MethodSource("lwaAuthSigner")
public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) { public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException {
LWAClient mockLWAClient = mock(LWAClient.class); LWAClient mockLWAClient = mock(LWAClient.class);
ArgumentCaptor<LWAAccessTokenRequestMeta> lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass(LWAAccessTokenRequestMeta.class); ArgumentCaptor<LWAAccessTokenRequestMeta> lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass(LWAAccessTokenRequestMeta.class);
@ -126,7 +126,7 @@ public class LWAAuthorizationSignerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaAuthSigner") @MethodSource("lwaAuthSigner")
public void returnSignedRequestWithAccessTokenFromLWAClient(String sellerType, LWAAuthorizationSigner testAuthSigner) { public void returnSignedRequestWithAccessTokenFromLWAClient(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException {
LWAClient mockLWAClient = mock(LWAClient.class); LWAClient mockLWAClient = mock(LWAClient.class);
when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class))) when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class)))
@ -140,7 +140,7 @@ public class LWAAuthorizationSignerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaAuthSigner") @MethodSource("lwaAuthSigner")
public void originalRequestIsImmutable(String sellerType, LWAAuthorizationSigner testAuthSigner) { public void originalRequestIsImmutable(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException {
LWAClient mockLWAClient = mock(LWAClient.class); LWAClient mockLWAClient = mock(LWAClient.class);
when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class))) when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class)))
@ -151,7 +151,7 @@ public class LWAAuthorizationSignerTest {
} }
@Test @Test
public void returnSignedRequestWithAccessTokenFromLWACache() throws IOException { public void returnSignedRequestWithAccessTokenFromLWACache() throws IOException, LWAException {
LWAClient testLWAClient = new LWAClient(TEST_ENDPOINT); LWAClient testLWAClient = new LWAClient(TEST_ENDPOINT);
testLWAClient.setOkHttpClient(mockOkHttpClient); testLWAClient.setOkHttpClient(mockOkHttpClient);

View File

@ -53,7 +53,10 @@ public class LWAClientTest {
private static final String SELLER_TYPE_SELLER = "seller"; private static final String SELLER_TYPE_SELLER = "seller";
private static final String SELLER_TYPE_SELLERLESS = "sellerless"; private static final String SELLER_TYPE_SELLERLESS = "sellerless";
private static final String LWA_EXCEPTION_MSG ="Error getting LWA Token";
private static final String LWA_CLIENT_AUTH_MSG ="Client_authentication_failed";
private static final String LWA_ACCESS_DENIED_MSG ="Authorization_server_denied_the_request";
@Mock @Mock
private OkHttpClient mockOkHttpClient; private OkHttpClient mockOkHttpClient;
@ -101,7 +104,7 @@ public class LWAClientTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaClient") @MethodSource("lwaClient")
public void makeRequestFromMeta (String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { public void makeRequestFromMeta (String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws LWAException,IOException {
ArgumentCaptor<Request> requestArgumentCaptor = ArgumentCaptor.forClass(Request.class); ArgumentCaptor<Request> requestArgumentCaptor = ArgumentCaptor.forClass(Request.class);
when(mockOkHttpClient.newCall(requestArgumentCaptor.capture())) when(mockOkHttpClient.newCall(requestArgumentCaptor.capture()))
.thenReturn(mockCall); .thenReturn(mockCall);
@ -134,7 +137,7 @@ public class LWAClientTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaClient") @MethodSource("lwaClient")
public void returnAccessTokenFromResponse(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { public void returnAccessTokenFromResponse(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException {
when(mockOkHttpClient.newCall(any(Request.class))) when(mockOkHttpClient.newCall(any(Request.class)))
.thenReturn(mockCall); .thenReturn(mockCall);
when(mockCall.execute()) when(mockCall.execute())
@ -145,20 +148,54 @@ public class LWAClientTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaClient") @MethodSource("lwaClient")
public void unsuccessfulPostThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { public void unsuccessfulPostThrowsLwaExceptionUnknownErrorCode(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException {
when(mockOkHttpClient.newCall(any(Request.class))) when(mockOkHttpClient.newCall(any(Request.class)))
.thenReturn(mockCall); .thenReturn(mockCall);
when(mockCall.execute()) when(mockCall.execute())
.thenReturn(buildResponse(400, "Azta|foo")); .thenReturn(buildResponse(400, "Azta|foo"));
Assertions.assertThrows(RuntimeException.class, () -> { Throwable exception = Assertions.assertThrows(LWAException.class, () -> {
underTest.getAccessToken(testLwaAccessTokenRequestMeta); underTest.getAccessToken(testLwaAccessTokenRequestMeta);
}); });
assertEquals(LWAExceptionErrorCode.other.toString(),((LWAException)exception).getErrorCode());
assertEquals(LWA_EXCEPTION_MSG, exception.getMessage());
} }
@ParameterizedTest @ParameterizedTest
@MethodSource("lwaClient") @MethodSource("lwaClient")
public void missingAccessTokenInResponseThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { public void unsuccessfulPostThrowsLwaExceptionKnownErrorCode1(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException {
when(mockOkHttpClient.newCall(any(Request.class)))
.thenReturn(mockCall);
when(mockCall.execute())
.thenReturn(buildResponse(401, "Azta|foo","invalid_client","Client_authentication_failed"));
Throwable exception = Assertions.assertThrows(LWAException.class, () -> {
underTest.getAccessToken(testLwaAccessTokenRequestMeta);
});
assertEquals(LWAExceptionErrorCode.invalid_client.toString(),((LWAException)exception).getErrorCode());
assertEquals(LWA_EXCEPTION_MSG, exception.getMessage());
assertEquals(LWA_CLIENT_AUTH_MSG,((LWAException)exception).getErrorMessage());
}
@ParameterizedTest
@MethodSource("lwaClient")
public void unsuccessfulPostThrowsLwaExceptionKnownErrorCode2(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException {
when(mockOkHttpClient.newCall(any(Request.class)))
.thenReturn(mockCall);
when(mockCall.execute())
.thenReturn(buildResponse(401, "Azta|foo","access_denied","Authorization_server_denied_the_request"));
Throwable exception = Assertions.assertThrows(LWAException.class, () -> {
underTest.getAccessToken(testLwaAccessTokenRequestMeta);
});
assertEquals(LWAExceptionErrorCode.access_denied.toString(),((LWAException)exception).getErrorCode());
assertEquals(LWA_EXCEPTION_MSG, exception.getMessage());
assertEquals(LWA_ACCESS_DENIED_MSG,((LWAException)exception).getErrorMessage());
}
@ParameterizedTest
@MethodSource("lwaClient")
public void missingAccessTokenInResponseThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException {
when(mockOkHttpClient.newCall(any(Request.class))) when(mockOkHttpClient.newCall(any(Request.class)))
.thenReturn(mockCall); .thenReturn(mockCall);
when(mockCall.execute()) when(mockCall.execute())
@ -171,7 +208,7 @@ public class LWAClientTest {
//Test for Access Token getting from cache //Test for Access Token getting from cache
@Test @Test
public void returnAccessTokenFromCache() throws IOException, InterruptedException { public void returnAccessTokenFromCache() throws IOException, InterruptedException, LWAException {
when(mockOkHttpClient.newCall(any(Request.class))) when(mockOkHttpClient.newCall(any(Request.class)))
.thenReturn(mockCall); .thenReturn(mockCall);
@ -187,7 +224,7 @@ public class LWAClientTest {
} }
@Test @Test
public void returnAccessTokenFromCacheWithExpiry() throws IOException, InterruptedException { public void returnAccessTokenFromCacheWithExpiry() throws IOException, InterruptedException, LWAException {
LWAClient client = new LWAClient(TEST_ENDPOINT); LWAClient client = new LWAClient(TEST_ENDPOINT);
client.setOkHttpClient(mockOkHttpClient); client.setOkHttpClient(mockOkHttpClient);
when(mockOkHttpClient.newCall(any(Request.class))) when(mockOkHttpClient.newCall(any(Request.class)))
@ -202,9 +239,9 @@ public class LWAClientTest {
assertEquals("Azta|foo1", client.getAccessToken(lwaAccessTokenRequestMetaSeller)); assertEquals("Azta|foo1", client.getAccessToken(lwaAccessTokenRequestMetaSeller));
} }
private static Response buildResponse(int code, String accessToken, String expiryInSeconds) { private static Response buildResponse(int code, String accessToken, String expiryInSeconds,String errorCode, String errorMessage) {
ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE, ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE,
String.format("{%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds)); String.format("{%s:%s,%s:%s,%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds, "error", errorCode, "error_description", errorMessage));
return new Response.Builder() return new Response.Builder()
.request(new Request.Builder().url(TEST_ENDPOINT).build()) .request(new Request.Builder().url(TEST_ENDPOINT).build())
@ -215,7 +252,24 @@ public class LWAClientTest {
.build(); .build();
} }
private static Response buildResponse(int code, String accessToken,String errorCode, String errorMessage) {
return buildResponse(code, accessToken, "3600",errorCode,errorMessage);
}
private static Response buildResponse(int code, String accessToken) { private static Response buildResponse(int code, String accessToken) {
return buildResponse(code, accessToken, "3600"); return buildResponse(code, accessToken, "3600");
} }
private static Response buildResponse(int code, String accessToken,String expiryInSeconds) {
ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE,
String.format("{%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds));
return new Response.Builder()
.request(new Request.Builder().url(TEST_ENDPOINT).build())
.code(code)
.body(responseBody)
.protocol(Protocol.HTTP_1_1)
.message("OK")
.build();
}
} }

View File

@ -1,247 +0,0 @@
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 getResourcePathWithPoundChar() {
testRequest = new Request.Builder()
.url("http://www.amazon.com/request/%23library")
.get()
.build();
underTest = new SignableRequestImpl(testRequest);
assertEquals("/request/%23library", 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<String, String> 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<String, List<String>> 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<String, List<String>> 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"));
}
}