I'm trying to call an get api which is hosted in aws api gateway via rest-assured
I'm able to sign the request and make a call. But to sign the request, I need to pass the full url to AWS to generate the Authorization Header.
For Ex. If I'm going to access an an endpoint
https://my-aws-api.com/basepath/v1/request/123
I need to sign the request via AWSSigner which needs the full endpoint to do so.
My current approach
String baseURI="https://my-aws-api.com";
String basePath="basepath/v1";
String requestPath="request/123";
String endpoint=baseURI+"/"+basePath+"/"+requestPath;
Map<String,String> signedHeaders= aws4sign(endpoint,defaultHeaders);
given()
.log().ifValidationFails()
.headers(signedHeaders)
.when()
.get(endpoint)
.then()
.log().ifValidationFails()
.statusCode(200);
If I do that , then I cant use RestAssured's baseURI, basePath and path params
I want to access it like
RestAssured.baseURI="https://my-aws-api.com";
RestAssured.basePath="basepath/v1";
given()
.log().ifValidationFails()
.pathParam("reqID", "123")
.when()
.get("request/{reqID}")
.then()
.log().ifValidationFails()
.statusCode(200);
AwsSigner
public static Map<String, String> aws4Sign(String endpoint, Map<String, String> headers) throws URISyntaxException {
String serviceName = "execute-api";
AWS4Signer aws4Signer = new AWS4Signer();
aws4Signer.setRegionName(EU_WEST_1.getName());
aws4Signer.setServiceName(serviceName);
DefaultRequest defaultRequest = new DefaultRequest(serviceName);
URI uri = new URI(endpoint);
defaultRequest.setEndpoint(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), "", "", ""));
defaultRequest.setHttpMethod(HttpMethodName.GET);
defaultRequest.setResourcePath(uri.getRawPath());
defaultRequest.setHeaders(headers);
aws4Signer.sign(defaultRequest, DefaultAWSCredentialsProviderChain.getInstance().getCredentials());
return defaultRequest.getHeaders();
}
So My question is there any way, I can intercept the RestAssured's request before it makes the call, so that I can get the fully generated end point and add the aws signed header to the call.
I am not familiar with this library but from briefly reading its documentation and Javadoc, you should be able to use a RequestFilter to inspect and alter a request before it is sent out.
Take a look at the Filter section of the user guide.
Thanks to #Ashaman.
The Filter Section is what I'm looking for
You can get the uri and other headers that were passed with requests from RequestSpec and then send it to the function to sign them and remove the old headers and put the new headers. Then forward the request
#BeforeAll
public void init() {
RestAssured.baseURI = "https://my-aws-api.com";
RestAssured.filters((requestSpec, responseSpec, ctx) -> {
Map<String, String> headers = requestSpec.getHeaders()
.asList()
.stream()
.collect(Collectors.toMap(Header::getName, Header::getValue));
Map<String, String> signedHeaders = aws4sign(requestSpec.getURI(), headers);
requestSpec.removeHeaders();
requestSpec.headers(signedHeaders);
return ctx.next(requestSpec, responseSpec);
});
}
And for the tests I can use the features of Rest Assured normally
given()
.log().ifValidationFails()
.pathParam("reqID", "123")
.when()
.get("request/{reqID}")
.then()
.log().ifValidationFails()
.statusCode(200);
Related
I would like to AWS sign my HTTP request fired by reactive WebClient of Spring. To sign the request I need access to the followings: URL, HTTP method, query parameters, headers and request body bytes.
I started with writing an ExchangeFilterFunction. Due to ClientRequest interface I can access everything there I need, except the request body:
#Component
public class AwsSigningInterceptor implements ExchangeFilterFunction
{
private final AwsHeaderSigner awsHeaderSigner;
public AwsSigningInterceptor(AwsHeaderSigner awsHeaderSigner)
{
this.awsHeaderSigner = awsHeaderSigner;
}
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next)
{
Map<String, List<String>> signingHeaders = awsHeaderSigner.createSigningHeaders(request, new byte[]{}, "es", "us-west-2"); // should pass request body bytes in place of new byte[]{}
ClientRequest.Builder requestBuilder = ClientRequest.from(request);
signingHeaders.forEach((key, value) -> requestBuilder.header(key, value.toArray(new String[0])));
return next.exchange(requestBuilder.build());
}
}
In older spring versions we used RestTemplate with a ClientHttpRequestInterceptor. In that case the bytes of the body were exposed, so signing was possible.
As I see in case of WebClient Spring handles the body as a Publisher, so I'm not sure if an ExchangeFilterFunction is a good place to start.
How should I sign the HTTP request?
My client is asking me to perform test for api where the body of the request should one in raw instead of header or something else.
He showed me how to do it in postman, but samething I want to simulate through automation which is in Java and rest assured.
Then How to approach the same in java becasue I can do it through get or post in java and restassured but dont know how to do it with raw.
GET-
#Test
public void mytest() {
Map<String, String> paramSample = new HashMap<>();
paramSample.put("v", "X.X");
paramSample.put("headers", "X");
paramSample.put("local", "X");
paramSample.put("method", "xxxxx");
paramSample.put("json", CreateJSON(ExecutionConfig.xxxx,0,0,ExecutionConfig.xxxxx,"Mex"));
paramSample.put("sender", ExecutionConfig.xxxx);
paramSample.put("api_key", ExecutionConfig.xxx);
paramSample.put("format", "json");
RequestSpecification request = given();
Response responseSample = request.params(paramSample).get(ExecutionConfig.Mxxxxx).then().extract().response();
I want to send http notifications with query params with Spring web client. I tried this:
WebClient client;
public Mono<Response> execute(String url) {
MultiValueMap map = new MultiValueMap<>();
map.add("some_key", "some_value");
return client.post().uri(builder -> builder
.host("http://www.test.com/notification")
.queryParams(map).build())
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(Response.class);
}
But I have several issues:
When I try to use MultiValueMap I get error Cannot instantiate the type MultiValueMap
Also how I can get the notification result? I don't want to send any payload or get any payload. I only want to get OK for response.
EDIT:
public Mono<String> execute(String url) {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("some_key", "some_value");
return client.post().uri(builder -> builder
.queryParams(map).build())
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(String.class);
}
MultiValueMap
MultiValueMap is an interface from org.springframework.util
You are trying to instantiate it the following way:
MultiValueMap map = new MultiValueMap<>();
This won't work as you need to provide an implementation of the interface. If you really want to instantiate it this way then maybe try using one of the implementations of the MultiValueMap, like LinkedMultiValueMap.
OK for response
If you are only interested in the result (i.e. the status code) of the request, you could try to do the following:
client.post().uri(builder -> builder
.host("http://www.test.com/notification")
.queryParams(map).build())
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.exchange()
.map(ClientResponse::statusCode);
The last call to map(...) will return an instance of Mono<HttpStatus>.
Hope this helps.
I need to invoke a form-data typed API using Rest Assured. Here is my code.
private Map<String, String> getFormParamsMap() {
Map<String, String> formParams = new HashMap<>();
formParams.put("creatorId", "Instructor1");
formParams.put("creatorPlatform", "Web");
formParams.put("creatoredSource", "File");
formParams.put("creatoredType", "Auto");
formParams.put("deckId", "5a605b472e02d86561172dad");
formParams.put("userId", "kind");
return formParams;
}
public void invoke() {
response = given()
.header("Content-Type", "application/form-data")
.header(AUTHORIZATION_HEADER_NAME, accessToken) //Some API contains access token to run with the API
.headers(headers)
.formParams(getFormParamsMap()) // requestParamsMap here.
.when()
.post(invokingEndpoint);
}
When I execute this, I am getting the below error.
Message: java.lang.IllegalArgumentException: Don't know how to encode creatorPlatform=Web&creatoredType=Auto&deckId=5a605b472e02d86561172dad&creatorId=Instructor1&creatoredSource=File&userId=kind as a byte stream.
Please use EncoderConfig (EncoderConfig#encodeContentTypeAs) to specify how to serialize data for this content-type.
For example: "given().config(RestAssured.config().encoderConfig(encoderConfig().encodeContentTypeAs("application/form-data", ContentType.TEXT))). .."
Stack Trace:
io.restassured.internal.http.EncoderRegistry.encodeStream(EncoderRegistry.java:130)
When I use .config(RestAssured.config().encoderConfig(encoderConfig().encodeContentTypeAs("application/form-data", ContentType.TEXT))) in the invoke() method, it gives the result as below.
{
"status": 400,
"message": "Content type 'application/x-www-form-urlencoded;charset=ISO-8859-1' not supported",
"error": "Bad Request",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException"
}
My request is not x-www-form-urlencoded type, it is form-data type. I can execute it using postman.
Appreciate your support on this.
Thanks.
I have solve this issue by using encodeContentTypeAs("multipart/form-data", ContentType.TEXT)
Ex:-
public void invoke() {
response = given()
.config(
RestAssured.config()
.encoderConfig(
encoderConfig()
.encodeContentTypeAs("multipart/form-data", ContentType.TEXT)))
.headers(headers)
.formParams(formParams)
.when()
.post(oAuthBaseURI).then().extract().response();
}
Please add the consumer as well.
See here for the encoders available for Rest Assured.
This might be causing the problem -
encodeContentTypeAs("application/form-data", ContentType.TEXT)
You can also try this -
.encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false).encodeContentTypeAs("application/form-data", ContentType.TEXT));
As far as I can tell, headers(headers) method replaces all headers, and then RestAssured uses x-www-form-urlencoded content type as default.
Try adding "Content-Type" header after the call to headers(headers).
I am using the latest okhttp version: okhttp-2.3.0.jar
How to add query parameters to GET request in okhttp in java ?
I found a related question about android, but no answer here!
For okhttp3:
private static final OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
public static void get(String url, Map<String,String>params, Callback responseCallback) {
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
if (params != null) {
for(Map.Entry<String, String> param : params.entrySet()) {
httpBuilder.addQueryParameter(param.getKey(),param.getValue());
}
}
Request request = new Request.Builder().url(httpBuilder.build()).build();
client.newCall(request).enqueue(responseCallback);
}
Here's my interceptor
private static class AuthInterceptor implements Interceptor {
private String mApiKey;
public AuthInterceptor(String apiKey) {
mApiKey = apiKey;
}
#Override
public Response intercept(Chain chain) throws IOException {
HttpUrl url = chain.request().url()
.newBuilder()
.addQueryParameter("api_key", mApiKey)
.build();
Request request = chain.request().newBuilder().url(url).build();
return chain.proceed(request);
}
}
I finally did my code, hope the following code can help you guys. I build the URL first using
HttpUrl httpUrl = new HttpUrl.Builder()
Then pass the URL to Request requesthttp hope it helps .
public class NetActions {
OkHttpClient client = new OkHttpClient();
public String getStudentById(String code) throws IOException, NullPointerException {
HttpUrl httpUrl = new HttpUrl.Builder()
.scheme("https")
.host("subdomain.apiweb.com")
.addPathSegment("api")
.addPathSegment("v1")
.addPathSegment("students")
.addPathSegment(code) // <- 8873 code passthru parameter on method
.addQueryParameter("auth_token", "71x23768234hgjwqguygqew")
// Each addPathSegment separated add a / symbol to the final url
// finally my Full URL is:
// https://subdomain.apiweb.com/api/v1/students/8873?auth_token=71x23768234hgjwqguygqew
.build();
System.out.println(httpUrl.toString());
Request requesthttp = new Request.Builder()
.addHeader("accept", "application/json")
.url(httpUrl) // <- Finally put httpUrl in here
.build();
Response response = client.newCall(requesthttp).execute();
return response.body().string();
}
}
As mentioned in the other answer, okhttp v2.4 offers new functionality that does make this possible.
See http://square.github.io/okhttp/2.x/okhttp/com/squareup/okhttp/HttpUrl.Builder.html#addQueryParameter-java.lang.String-java.lang.String-
This is not possible with the current version of okhttp, there is no method provided that will handle this for you.
The next best thing is building an url string or an URL object (found in java.net.URL) with the query included yourself, and pass that to the request builder of okhttp.
As you can see, the Request.Builder can take either a String or an URL.
Examples on how to build an url can be found at What is the idiomatic way to compose a URL or URI in Java?
As of right now (okhttp 2.4), HttpUrl.Builder now has methods addQueryParameter and addEncodedQueryParameter.
You can create a newBuilder from existing HttoUrl and add query parameters there. Sample interceptor code:
Request req = it.request()
return chain.proceed(
req.newBuilder()
.url(
req.url().newBuilder()
.addQueryParameter("v", "5.60")
.build());
.build());
Use HttpUrl class's functions:
//adds the pre-encoded query parameter to this URL's query string
addEncodedQueryParameter(String encodedName, String encodedValue)
//encodes the query parameter using UTF-8 and adds it to this URL's query string
addQueryParameter(String name, String value)
more detailed: https://stackoverflow.com/a/32146909/5247331