diff --git a/src/main/java/com/ibm/cloud/sdk/core/http/RequestBuilder.java b/src/main/java/com/ibm/cloud/sdk/core/http/RequestBuilder.java index b97c6657b..2f126e4a8 100644 --- a/src/main/java/com/ibm/cloud/sdk/core/http/RequestBuilder.java +++ b/src/main/java/com/ibm/cloud/sdk/core/http/RequestBuilder.java @@ -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 @@ -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. */ @@ -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: + *
    + *
  1. If 'jsonContent' is not null, then use that.
  2. + *
  3. If 'jsonPatchContent' is not null, then use that.
  4. + *
  5. Else use 'nonJsonContent'. + *
* * @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); @@ -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: + *
    + *
  1. If 'jsonContent' is not null, then use that.
  2. + *
  3. If 'jsonPatchContent' is not null, then use that.
  4. + *
  5. Else use 'nonJsonContent'. + *
* * @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, diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/service/RequestBuilderTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/service/RequestBuilderTest.java index f6cc86c20..7be777c0a 100644 --- a/src/test/java/com/ibm/cloud/sdk/core/test/service/RequestBuilderTest.java +++ b/src/test/java/com/ibm/cloud/sdk/core/test/service/RequestBuilderTest.java @@ -293,7 +293,7 @@ public void testConstructHttpUrlEmptyPath1() { assertNotNull(url); assertEquals("https://myserver.com/testservice/api/discovery", url.toString()); } - + @Test public void testConstructHttpUrlEmptyPath2() { String[] pathSegments = { "" }; @@ -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()); + } + }