Skip to content

Commit

Permalink
feat: RequestBuilder changes to support JSON streaming feature
Browse files Browse the repository at this point in the history
Fixes arf/planning-sdk-squad#901

The RequestBuilder.bodyContent() method is modified to no
longer use the contentType parameter to determine which one of the
other three input parameters should be used to create the request body.
Instead, we now use the first non-null value using the order
'jsonContent', 'jsonPatchContent', then 'nonJsonContent'.

This change is necessary for us to implement the JSON streaming
feature, where the SDK user might serialize the JSON content
himself and pass it in as a String or InputStream while also
using a contentType that would otherwise indicate a JSON body.
  • Loading branch information
padamstx committed Jun 14, 2020
1 parent 3b55ac6 commit e349979
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 21 deletions.
49 changes: 31 additions & 18 deletions src/main/java/com/ibm/cloud/sdk/core/http/RequestBuilder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* (C) Copyright IBM Corp. 2015, 2019.
* (C) Copyright IBM Corp. 2015, 2020.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
Expand All @@ -13,22 +13,22 @@

package com.ibm.cloud.sdk.core.http;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ibm.cloud.sdk.core.service.BaseService;
import com.ibm.cloud.sdk.core.util.GsonSingleton;
import com.ibm.cloud.sdk.core.util.StringHelper;
import com.ibm.cloud.sdk.core.util.Validator;

import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
* Convenience class for constructing HTTP/HTTPS requests.
*/
Expand Down Expand Up @@ -325,26 +325,33 @@ public RequestBuilder bodyContent(InputStream stream, String contentType) {
return body(InputStreamRequestBody.create(MediaType.parse(contentType), stream));
}


/**
* Sets the request body content from one of three different sources, based on the content type.
* Sets the request body content from one of three different sources.
* The three input sources are used in this precedence order:
* <ol>
* <li>If 'jsonContent' is not null, then use that.</li>
* <li>If 'jsonPatchContent' is not null, then use that.</li>
* <li>Else use 'nonJsonContent'.
* </ol>
*
* @param contentType
* the value of the "Content-Type" header associated with the outgoing request
* the value of the "Content-Type" header associated with the request body
* @param jsonContent
* the body content to be used if the content type indicates JSON
* a model instance to be serialized and used for the request body
* @param jsonPatchContent
* the body content to be used if the content type indicates JsonPatch
* a collection of JsonPatchOperation instances to be serialized and used for the request body
* @param nonJsonContent
* the body content to be used if the content type indicates non-JSON content
* an InputStream whose contents should be used directly as the request body
* @return this
*/
public RequestBuilder bodyContent(String contentType, Object jsonContent, Object jsonPatchContent,
InputStream nonJsonContent) {
if (contentType != null) {
Gson requestGson = GsonSingleton.getGsonWithoutPrettyPrinting().newBuilder().create();
if (BaseService.isJsonMimeType(contentType)) {
if (jsonContent != null) {
this.bodyContent(requestGson.toJson(jsonContent), contentType);
} else if (BaseService.isJsonPatchMimeType(contentType)) {
} else if (jsonPatchContent != null) {
this.bodyContent(requestGson.toJson(jsonPatchContent), contentType);
} else {
this.bodyContent(nonJsonContent, contentType);
Expand All @@ -354,16 +361,22 @@ public RequestBuilder bodyContent(String contentType, Object jsonContent, Object
}

/**
* Sets the request body content from one of three different sources, based on the content type.
* Sets the request body content from one of three different sources.
* The three input sources are used in this precedence order:
* <ol>
* <li>If 'jsonContent' is not null, then use that.</li>
* <li>If 'jsonPatchContent' is not null, then use that.</li>
* <li>Else use 'nonJsonContent'.
* </ol>
*
* @param contentType
* the value of the "Content-Type" header associated with the outgoing request
* the value of the "Content-Type" header associated with the request body
* @param jsonContent
* the body content to be used if the content type indicates JSON
* a model instance to be serialized and used for the request body
* @param jsonPatchContent
* the body content to be used if the content type indicates JsonPatch
* a collection of JsonPatchOperation instances to be serialized and used for the request body
* @param nonJsonContent
* the body content to be used if the content type indicates non-JSON content
* a string to be used directly as the request body
* @return this
*/
public RequestBuilder bodyContent(String contentType, Object jsonContent, Object jsonPatchContent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ public void testConstructHttpUrlEmptyPath1() {
assertNotNull(url);
assertEquals("https://myserver.com/testservice/api/discovery", url.toString());
}

@Test
public void testConstructHttpUrlEmptyPath2() {
String[] pathSegments = { "" };
Expand Down Expand Up @@ -333,13 +333,66 @@ public void testConstructHttpUrlEmptyPathAndParams() {
public void testConstructHttpUrlEmpty() {
String[] pathSegments = { "v1/seg1", "seg2", "seg3"};
String[] pathParameters = { "param1", "param2" };
HttpUrl url = RequestBuilder.constructHttpUrl("", pathSegments, pathParameters);
RequestBuilder.constructHttpUrl("", pathSegments, pathParameters);
}

@Test(expected = IllegalArgumentException.class)
public void testConstructHttpUrlNull() {
String[] pathSegments = { "v1/seg1", "seg2", "seg3"};
String[] pathParameters = { "param1", "param2" };
HttpUrl url = RequestBuilder.constructHttpUrl(null, pathSegments, pathParameters);
RequestBuilder.constructHttpUrl(null, pathSegments, pathParameters);
}

/**
* Test bodyContent() with a model instance.
* @throws IOException
*/
@Test
public void testBodyContent1() throws IOException {
Truck truck = new Truck.Builder().vehicleType("Truck").make("Ford").engineType("raptor").build();
final Request request = RequestBuilder.post(HttpUrl.parse(urlWithQuery))
.bodyContent("application/json", truck, null, (InputStream) null).build();
final RequestBody requestedBody = request.body();
final Buffer buffer = new Buffer();
requestedBody.writeTo(buffer);

assertEquals(GsonSingleton.getGsonWithoutPrettyPrinting().toJson(truck), buffer.readUtf8());
assertEquals(HttpMediaType.JSON, requestedBody.contentType());
}

/**
* Test bodyContent() with an already serialized model instance (a String).
* @throws IOException
*/
@Test
public void testBodyContent2() throws IOException {
Truck truck = new Truck.Builder().vehicleType("Truck").make("Ford").engineType("raptor").build();
String jsonString = GsonSingleton.getGsonWithoutPrettyPrinting().toJson(truck);
final Request request = RequestBuilder.post(HttpUrl.parse(urlWithQuery))
.bodyContent("application/json", null, null, jsonString).build();
final RequestBody requestedBody = request.body();
final Buffer buffer = new Buffer();
requestedBody.writeTo(buffer);

assertEquals(jsonString, buffer.readUtf8());
assertEquals(MediaType.parse("application/json"), requestedBody.contentType());
}

/**
* Test bodyContent() with a multiple inputs (JSON input should win).
* @throws IOException
*/
@Test
public void testBodyContent3() throws IOException {
Truck truck = new Truck.Builder().vehicleType("Truck").make("Ford").engineType("raptor").build();
final Request request = RequestBuilder.post(HttpUrl.parse(urlWithQuery))
.bodyContent("application/json", truck, "BAD JSON PATCH BODY", "BAD INPUTSTREAM REQUEST BODY").build();
final RequestBody requestedBody = request.body();
final Buffer buffer = new Buffer();
requestedBody.writeTo(buffer);

assertEquals(GsonSingleton.getGsonWithoutPrettyPrinting().toJson(truck), buffer.readUtf8());
assertEquals(HttpMediaType.JSON, requestedBody.contentType());
}

}

0 comments on commit e349979

Please sign in to comment.