Skip to content

Commit

Permalink
feat(java): add requestOptions (#487)
Browse files Browse the repository at this point in the history
  • Loading branch information
shortcuts authored May 11, 2022
1 parent aae59b9 commit c5ac185
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 104 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.algolia.utils;

import com.algolia.ApiClient;
import com.algolia.exceptions.*;
import com.algolia.utils.retry.RetryStrategy;
import com.algolia.utils.retry.StatefulHost;
Expand Down Expand Up @@ -99,20 +98,8 @@ private <T> T deserialize(Response response, Type returnType)
if (contentType == null) {
contentType = "application/json";
}
if (ApiClient.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 AlgoliaApiException(
"Content type \"" +
contentType +
"\" is not supported for type: " +
returnType,
response.code()
);
}

return JSON.deserialize(respBody, returnType);
}

public void setDebugging(boolean debugging) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.algolia.utils;

import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;

/**
* Request options are used to pass extra parameters, headers, timeout to the request. Parameters
* set in the request option will override default parameter.
*/
public class RequestOptions {

private final Map<String, String> headers = new HashMap<String, String>();
private final Map<String, String> queryParams = new HashMap<String, String>();
private Integer timeout = null;

public RequestOptions addExtraHeader(
@Nonnull String key,
@Nonnull String value
) {
headers.put(key, value);
return this;
}

public RequestOptions addExtraQueryParameters(
@Nonnull String key,
@Nonnull String value
) {
queryParams.put(key, value);
return this;
}

public Map<String, String> getExtraHeaders() {
return headers;
}

public Map<String, String> getExtraQueryParams() {
return queryParams;
}

public Integer getTimeout() {
return timeout;
}

public RequestOptions setTimeout(Integer timeout) {
this.timeout = timeout;
return this;
}

@Override
public String toString() {
return (
"RequestOptions{" +
"headers=" +
headers +
", queryParams=" +
queryParams +
'\'' +
'}'
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,13 +240,13 @@ export function createTransporter({
cacheable: baseRequestOptions?.cacheable,
timeout: baseRequestOptions?.timeout,
queryParameters: {
...baseRequestOptions?.queryParameters,
...methodOptions.queryParameters,
...baseRequestOptions?.queryParameters,
},
headers: {
Accept: 'application/json',
...baseRequestOptions?.headers,
...methodOptions.headers,
...baseRequestOptions?.headers,
},
};

Expand Down
181 changes: 99 additions & 82 deletions templates/java/libraries/okhttp-gson/ApiClient.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.algolia.utils.Requester;
import com.algolia.exceptions.*;
import com.algolia.utils.UserAgent;
import com.algolia.utils.JSON;
import com.algolia.utils.RequestOptions;

import okhttp3.*;
import okhttp3.internal.http.HttpMethod;
Expand All @@ -27,8 +28,7 @@ public class ApiClient {
private boolean debugging = false;
private Map<String, String> defaultHeaderMap = new HashMap<String, String>();
private String appId, apiKey;
private String contentType;
private DateFormat dateFormat;
Expand All @@ -38,6 +38,8 @@ public class ApiClient {
* Constructor for ApiClient with custom Requester
*/
public ApiClient(String appId, String apiKey, Requester requester, String clientName, UserAgent.Segment[] segments) {
this.contentType = "application/json";
UserAgent ua = new UserAgent("{{packageVersion}}");
ua.addSegment(new UserAgent.Segment(clientName, "{{packageVersion}}"));
if(segments != null) {
Expand All @@ -47,8 +49,11 @@ public class ApiClient {
}
setUserAgent(ua.toString());

this.appId = appId;
this.apiKey = apiKey;
defaultHeaderMap.put("X-Algolia-Application-Id", appId);
defaultHeaderMap.put("X-Algolia-API-Key", apiKey);
defaultHeaderMap.put("Accept", this.contentType);
defaultHeaderMap.put("Content-Type", this.contentType);

this.requester = requester;
}

Expand Down Expand Up @@ -189,22 +194,6 @@ public class ApiClient {
}
}
/**
* 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 static boolean isJsonMime(String mime) {
String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
return mime != null && (mime.matches(jsonMime) || mime.equals("*/*"));
}
/**
* Escape the given string to be used as URL query value.
*
Expand All @@ -220,29 +209,23 @@ public class ApiClient {
}
/**
* Serialize the given Java object into request body according to the object's
* class and the request Content-Type.
* 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 AlgoliaRuntimeException If fail to serialize the given object
*/
public RequestBody serialize(Object obj, String contentType) throws AlgoliaRuntimeException {
if (obj instanceof byte[]) {
// Binary (byte array) body parameter support.
return RequestBody.create((byte[]) obj, MediaType.parse(contentType));
} else if (isJsonMime(contentType)) {
String content;
if (obj != null) {
content = JSON.serialize(obj);
} else {
content = null;
}
return RequestBody.create(content, MediaType.parse(contentType));
} else {
throw new AlgoliaRuntimeException("Content type \"" + contentType + "\" is not supported");
}
public RequestBody serialize(Object obj) throws AlgoliaRuntimeException {
String content;
if (obj != null) {
content = JSON.serialize(obj);
} else {
content = null;
}
return RequestBody.create(content, MediaType.parse(this.contentType));
}
/**
Expand Down Expand Up @@ -286,11 +269,12 @@ public class ApiClient {
* @param queryParams The query parameters
* @param body The request body object
* @param headerParams The header parameters
* @param requestOptions The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
* @return The HTTP call
* @throws AlgoliaRuntimeException If fail to serialize the request body object
*/
public Call buildCall(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams) throws AlgoliaRuntimeException {
Request request = buildRequest(path, method, queryParams, body, headerParams);
public Call buildCall(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, RequestOptions requestOptions) throws AlgoliaRuntimeException {
Request request = buildRequest(path, method, queryParams, body, headerParams, requestOptions);
return requester.newCall(request);
}
Expand All @@ -303,37 +287,38 @@ public class ApiClient {
* @param queryParams The query parameters
* @param body The request body object
* @param headerParams The header parameters
* @param requestOptions The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
* @return The HTTP request
* @throws AlgoliaRuntimeException If fail to serialize the request body object
*/
public Request buildRequest(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams) throws AlgoliaRuntimeException {
headerParams.put("X-Algolia-Application-Id", this.appId);
headerParams.put("X-Algolia-API-Key", this.apiKey);
headerParams.put("Accept", "application/json");
headerParams.put("Content-Type", "application/json");
String contentType = "application/json";
headerParams.put("Accept", contentType);
headerParams.put("Content-Type", contentType);
final String url = buildUrl(path, queryParams);
public Request buildRequest(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, RequestOptions requestOptions) throws AlgoliaRuntimeException {
boolean hasRequestOptions = requestOptions != null;
final String url = buildUrl(
path,
queryParams,
hasRequestOptions ? requestOptions.getExtraQueryParams() : null
);
final Request.Builder reqBuilder = new Request.Builder().url(url);
processHeaderParams(headerParams, reqBuilder);
processHeaderParams(
headerParams,
hasRequestOptions ? requestOptions.getExtraHeaders() : null,
reqBuilder
);
RequestBody reqBody;
if (!HttpMethod.permitsRequestBody(method)) {
reqBody = null;
} else if (body == null) {
if ("DELETE".equals(method)) {
// allow calling DELETE without sending a request body
reqBody = null;
// 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));
// use an empty request body (for POST, PUT and PATCH)
reqBody = RequestBody.create("", MediaType.parse(this.contentType));
}
} else {
reqBody = serialize(body, contentType);
}
} else {
reqBody = serialize(body);
}
return reqBuilder.method(method, reqBody).build();
}
Expand All @@ -343,48 +328,80 @@ public class ApiClient {
*
* @param path The sub path
* @param queryParams The query parameters
* @param extraQueryParams The query parameters, coming from the requestOptions
* @return The full URL
*/
public String buildUrl(String path, Map<String, String> queryParams) {
final StringBuilder url = new StringBuilder();
public String buildUrl(String path, Map<String, String> queryParams, Map<String, String> extraQueryParams) {
StringBuilder url = new StringBuilder();
//The real host will be assigned by the retry strategy
url.append("http://temp.path").append(path);
if (queryParams != null && !queryParams.isEmpty()) {
// support (constant) query string in `path`, e.g. "/posts?draft=1"
String prefix = path.contains("?") ? "&" : "?";
for (Entry<String, String> param : queryParams.entrySet()) {
if (param.getValue() != null) {
if (prefix != null) {
url.append(prefix);
prefix = null;
} else {
url.append("&");
}
String value = parameterToString(param.getValue());
url.append(escapeString(param.getKey())).append("=").append(escapeString(value));
}
url = parseQueryParameters(path, url, queryParams);
url = parseQueryParameters(path, url, extraQueryParams);
return url.toString();
}
/**
* Parses the given map of Query Parameters to a given URL.
*
* @param path The sub path
* @param url The url to add queryParams to
* @param queryParams The query parameters
* @return The URL
*/
public StringBuilder parseQueryParameters(
String path,
StringBuilder url,
Map<String, String> queryParams
) {
if (queryParams != null && !queryParams.isEmpty()) {
// support (constant) query string in `path`, e.g. "/posts?draft=1"
String prefix = path.contains("?") ? "&" : "?";
for (Entry<String, String> param : queryParams.entrySet()) {
if (param.getValue() != null) {
if (prefix != null) {
url.append(prefix);
prefix = null;
} else {
url.append("&");
}
String value = parameterToString(param.getValue());
url
.append(escapeString(param.getKey()))
.append("=")
.append(escapeString(value));
}
}
}
return url.toString();
return url;
}
/**
* Set header parameters to the request builder, including default headers.
*
* @param headerParams Header parameters in the form of Map
* @param extraHeaderParams Header parameters in the form of Map, coming from RequestOptions
* @param reqBuilder Request.Builder
*/
public void processHeaderParams(Map<String, String> headerParams, Request.Builder reqBuilder) {
public void processHeaderParams(Map<String, String> headerParams, Map<String, String> extraHeaderParams, Request.Builder reqBuilder) {
for (Entry<String, String> param : headerParams.entrySet()) {
reqBuilder.header(param.getKey(), parameterToString(param.getValue()));
reqBuilder.header(param.getKey(), parameterToString(param.getValue()));
}
for (Entry<String, String> header : defaultHeaderMap.entrySet()) {
if (!headerParams.containsKey(header.getKey())) {
reqBuilder.header(header.getKey(), parameterToString(header.getValue()));
}
if (!headerParams.containsKey(header.getKey())) {
reqBuilder.header(header.getKey(), parameterToString(header.getValue()));
}
}
if (extraHeaderParams != null) {
for (Entry<String, String> header : extraHeaderParams.entrySet()) {
reqBuilder.header(
header.getKey(),
parameterToString(header.getValue())
);
}
}
}
Expand Down
Loading

0 comments on commit c5ac185

Please sign in to comment.