Skip to content

Commit c5ac185

Browse files
authored
feat(java): add requestOptions (#487)
1 parent aae59b9 commit c5ac185

File tree

5 files changed

+205
-104
lines changed

5 files changed

+205
-104
lines changed

clients/algoliasearch-client-java-2/algoliasearch-core/src/main/java/com/algolia/utils/HttpRequester.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.algolia.utils;
22

3-
import com.algolia.ApiClient;
43
import com.algolia.exceptions.*;
54
import com.algolia.utils.retry.RetryStrategy;
65
import com.algolia.utils.retry.StatefulHost;
@@ -99,20 +98,8 @@ private <T> T deserialize(Response response, Type returnType)
9998
if (contentType == null) {
10099
contentType = "application/json";
101100
}
102-
if (ApiClient.isJsonMime(contentType)) {
103-
return JSON.deserialize(respBody, returnType);
104-
} else if (returnType.equals(String.class)) {
105-
// Expecting string, return the raw response body.
106-
return (T) respBody;
107-
} else {
108-
throw new AlgoliaApiException(
109-
"Content type \"" +
110-
contentType +
111-
"\" is not supported for type: " +
112-
returnType,
113-
response.code()
114-
);
115-
}
101+
102+
return JSON.deserialize(respBody, returnType);
116103
}
117104

118105
public void setDebugging(boolean debugging) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.algolia.utils;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import javax.annotation.Nonnull;
6+
7+
/**
8+
* Request options are used to pass extra parameters, headers, timeout to the request. Parameters
9+
* set in the request option will override default parameter.
10+
*/
11+
public class RequestOptions {
12+
13+
private final Map<String, String> headers = new HashMap<String, String>();
14+
private final Map<String, String> queryParams = new HashMap<String, String>();
15+
private Integer timeout = null;
16+
17+
public RequestOptions addExtraHeader(
18+
@Nonnull String key,
19+
@Nonnull String value
20+
) {
21+
headers.put(key, value);
22+
return this;
23+
}
24+
25+
public RequestOptions addExtraQueryParameters(
26+
@Nonnull String key,
27+
@Nonnull String value
28+
) {
29+
queryParams.put(key, value);
30+
return this;
31+
}
32+
33+
public Map<String, String> getExtraHeaders() {
34+
return headers;
35+
}
36+
37+
public Map<String, String> getExtraQueryParams() {
38+
return queryParams;
39+
}
40+
41+
public Integer getTimeout() {
42+
return timeout;
43+
}
44+
45+
public RequestOptions setTimeout(Integer timeout) {
46+
this.timeout = timeout;
47+
return this;
48+
}
49+
50+
@Override
51+
public String toString() {
52+
return (
53+
"RequestOptions{" +
54+
"headers=" +
55+
headers +
56+
", queryParams=" +
57+
queryParams +
58+
'\'' +
59+
'}'
60+
);
61+
}
62+
}

clients/algoliasearch-client-javascript/packages/client-common/src/transporter/createTransporter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,13 @@ export function createTransporter({
240240
cacheable: baseRequestOptions?.cacheable,
241241
timeout: baseRequestOptions?.timeout,
242242
queryParameters: {
243-
...baseRequestOptions?.queryParameters,
244243
...methodOptions.queryParameters,
244+
...baseRequestOptions?.queryParameters,
245245
},
246246
headers: {
247247
Accept: 'application/json',
248-
...baseRequestOptions?.headers,
249248
...methodOptions.headers,
249+
...baseRequestOptions?.headers,
250250
},
251251
};
252252

templates/java/libraries/okhttp-gson/ApiClient.mustache

Lines changed: 99 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.algolia.utils.Requester;
44
import com.algolia.exceptions.*;
55
import com.algolia.utils.UserAgent;
66
import com.algolia.utils.JSON;
7+
import com.algolia.utils.RequestOptions;
78

89
import okhttp3.*;
910
import okhttp3.internal.http.HttpMethod;
@@ -27,8 +28,7 @@ public class ApiClient {
2728
private boolean debugging = false;
2829
private Map<String, String> defaultHeaderMap = new HashMap<String, String>();
2930
30-
31-
private String appId, apiKey;
31+
private String contentType;
3232
3333
private DateFormat dateFormat;
3434
@@ -38,6 +38,8 @@ public class ApiClient {
3838
* Constructor for ApiClient with custom Requester
3939
*/
4040
public ApiClient(String appId, String apiKey, Requester requester, String clientName, UserAgent.Segment[] segments) {
41+
this.contentType = "application/json";
42+
4143
UserAgent ua = new UserAgent("{{packageVersion}}");
4244
ua.addSegment(new UserAgent.Segment(clientName, "{{packageVersion}}"));
4345
if(segments != null) {
@@ -47,8 +49,11 @@ public class ApiClient {
4749
}
4850
setUserAgent(ua.toString());
4951

50-
this.appId = appId;
51-
this.apiKey = apiKey;
52+
defaultHeaderMap.put("X-Algolia-Application-Id", appId);
53+
defaultHeaderMap.put("X-Algolia-API-Key", apiKey);
54+
defaultHeaderMap.put("Accept", this.contentType);
55+
defaultHeaderMap.put("Content-Type", this.contentType);
56+
5257
this.requester = requester;
5358
}
5459

@@ -189,22 +194,6 @@ public class ApiClient {
189194
}
190195
}
191196
192-
/**
193-
* Check if the given MIME is a JSON MIME.
194-
* JSON MIME examples:
195-
* application/json
196-
* application/json; charset=UTF8
197-
* APPLICATION/JSON
198-
* application/vnd.company+json
199-
* "* / *" is also default to JSON
200-
* @param mime MIME (Multipurpose Internet Mail Extensions)
201-
* @return True if the given MIME is JSON, false otherwise.
202-
*/
203-
public static boolean isJsonMime(String mime) {
204-
String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
205-
return mime != null && (mime.matches(jsonMime) || mime.equals("*/*"));
206-
}
207-
208197
/**
209198
* Escape the given string to be used as URL query value.
210199
*
@@ -220,29 +209,23 @@ public class ApiClient {
220209
}
221210
222211
/**
223-
* Serialize the given Java object into request body according to the object's
224-
* class and the request Content-Type.
212+
* Serialize the given Java object into request body according to the object's class and the
213+
* request Content-Type.
225214
*
226215
* @param obj The Java object
227-
* @param contentType The request Content-Type
228216
* @return The serialized request body
229217
* @throws AlgoliaRuntimeException If fail to serialize the given object
230218
*/
231-
public RequestBody serialize(Object obj, String contentType) throws AlgoliaRuntimeException {
232-
if (obj instanceof byte[]) {
233-
// Binary (byte array) body parameter support.
234-
return RequestBody.create((byte[]) obj, MediaType.parse(contentType));
235-
} else if (isJsonMime(contentType)) {
236-
String content;
237-
if (obj != null) {
238-
content = JSON.serialize(obj);
239-
} else {
240-
content = null;
241-
}
242-
return RequestBody.create(content, MediaType.parse(contentType));
243-
} else {
244-
throw new AlgoliaRuntimeException("Content type \"" + contentType + "\" is not supported");
245-
}
219+
public RequestBody serialize(Object obj) throws AlgoliaRuntimeException {
220+
String content;
221+
222+
if (obj != null) {
223+
content = JSON.serialize(obj);
224+
} else {
225+
content = null;
226+
}
227+
228+
return RequestBody.create(content, MediaType.parse(this.contentType));
246229
}
247230
248231
/**
@@ -286,11 +269,12 @@ public class ApiClient {
286269
* @param queryParams The query parameters
287270
* @param body The request body object
288271
* @param headerParams The header parameters
272+
* @param requestOptions The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
289273
* @return The HTTP call
290274
* @throws AlgoliaRuntimeException If fail to serialize the request body object
291275
*/
292-
public Call buildCall(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams) throws AlgoliaRuntimeException {
293-
Request request = buildRequest(path, method, queryParams, body, headerParams);
276+
public Call buildCall(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, RequestOptions requestOptions) throws AlgoliaRuntimeException {
277+
Request request = buildRequest(path, method, queryParams, body, headerParams, requestOptions);
294278
295279
return requester.newCall(request);
296280
}
@@ -303,37 +287,38 @@ public class ApiClient {
303287
* @param queryParams The query parameters
304288
* @param body The request body object
305289
* @param headerParams The header parameters
290+
* @param requestOptions The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
306291
* @return The HTTP request
307292
* @throws AlgoliaRuntimeException If fail to serialize the request body object
308293
*/
309-
public Request buildRequest(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams) throws AlgoliaRuntimeException {
310-
headerParams.put("X-Algolia-Application-Id", this.appId);
311-
headerParams.put("X-Algolia-API-Key", this.apiKey);
312-
headerParams.put("Accept", "application/json");
313-
headerParams.put("Content-Type", "application/json");
314-
315-
String contentType = "application/json";
316-
headerParams.put("Accept", contentType);
317-
headerParams.put("Content-Type", contentType);
318-
319-
final String url = buildUrl(path, queryParams);
294+
public Request buildRequest(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, RequestOptions requestOptions) throws AlgoliaRuntimeException {
295+
boolean hasRequestOptions = requestOptions != null;
296+
final String url = buildUrl(
297+
path,
298+
queryParams,
299+
hasRequestOptions ? requestOptions.getExtraQueryParams() : null
300+
);
320301
final Request.Builder reqBuilder = new Request.Builder().url(url);
321-
processHeaderParams(headerParams, reqBuilder);
322-
302+
processHeaderParams(
303+
headerParams,
304+
hasRequestOptions ? requestOptions.getExtraHeaders() : null,
305+
reqBuilder
306+
);
307+
323308
RequestBody reqBody;
324309
if (!HttpMethod.permitsRequestBody(method)) {
325310
reqBody = null;
326311
} else if (body == null) {
327312
if ("DELETE".equals(method)) {
328-
// allow calling DELETE without sending a request body
329-
reqBody = null;
313+
// allow calling DELETE without sending a request body
314+
reqBody = null;
330315
} else {
331-
// use an empty request body (for POST, PUT and PATCH)
332-
reqBody = RequestBody.create("", MediaType.parse(contentType));
316+
// use an empty request body (for POST, PUT and PATCH)
317+
reqBody = RequestBody.create("", MediaType.parse(this.contentType));
333318
}
334-
} else {
335-
reqBody = serialize(body, contentType);
336-
}
319+
} else {
320+
reqBody = serialize(body);
321+
}
337322
338323
return reqBuilder.method(method, reqBody).build();
339324
}
@@ -343,48 +328,80 @@ public class ApiClient {
343328
*
344329
* @param path The sub path
345330
* @param queryParams The query parameters
331+
* @param extraQueryParams The query parameters, coming from the requestOptions
346332
* @return The full URL
347333
*/
348-
public String buildUrl(String path, Map<String, String> queryParams) {
349-
final StringBuilder url = new StringBuilder();
334+
public String buildUrl(String path, Map<String, String> queryParams, Map<String, String> extraQueryParams) {
335+
StringBuilder url = new StringBuilder();
350336
351337
//The real host will be assigned by the retry strategy
352338
url.append("http://temp.path").append(path);
353339
354-
if (queryParams != null && !queryParams.isEmpty()) {
355-
// support (constant) query string in `path`, e.g. "/posts?draft=1"
356-
String prefix = path.contains("?") ? "&" : "?";
357-
for (Entry<String, String> param : queryParams.entrySet()) {
358-
if (param.getValue() != null) {
359-
if (prefix != null) {
360-
url.append(prefix);
361-
prefix = null;
362-
} else {
363-
url.append("&");
364-
}
365-
String value = parameterToString(param.getValue());
366-
url.append(escapeString(param.getKey())).append("=").append(escapeString(value));
367-
}
340+
url = parseQueryParameters(path, url, queryParams);
341+
url = parseQueryParameters(path, url, extraQueryParams);
342+
343+
return url.toString();
344+
}
345+
346+
/**
347+
* Parses the given map of Query Parameters to a given URL.
348+
*
349+
* @param path The sub path
350+
* @param url The url to add queryParams to
351+
* @param queryParams The query parameters
352+
* @return The URL
353+
*/
354+
public StringBuilder parseQueryParameters(
355+
String path,
356+
StringBuilder url,
357+
Map<String, String> queryParams
358+
) {
359+
if (queryParams != null && !queryParams.isEmpty()) {
360+
// support (constant) query string in `path`, e.g. "/posts?draft=1"
361+
String prefix = path.contains("?") ? "&" : "?";
362+
for (Entry<String, String> param : queryParams.entrySet()) {
363+
if (param.getValue() != null) {
364+
if (prefix != null) {
365+
url.append(prefix);
366+
prefix = null;
367+
} else {
368+
url.append("&");
368369
}
370+
String value = parameterToString(param.getValue());
371+
url
372+
.append(escapeString(param.getKey()))
373+
.append("=")
374+
.append(escapeString(value));
375+
}
369376
}
377+
}
370378
371-
return url.toString();
379+
return url;
372380
}
373381
374382
/**
375383
* Set header parameters to the request builder, including default headers.
376384
*
377385
* @param headerParams Header parameters in the form of Map
386+
* @param extraHeaderParams Header parameters in the form of Map, coming from RequestOptions
378387
* @param reqBuilder Request.Builder
379388
*/
380-
public void processHeaderParams(Map<String, String> headerParams, Request.Builder reqBuilder) {
389+
public void processHeaderParams(Map<String, String> headerParams, Map<String, String> extraHeaderParams, Request.Builder reqBuilder) {
381390
for (Entry<String, String> param : headerParams.entrySet()) {
382-
reqBuilder.header(param.getKey(), parameterToString(param.getValue()));
391+
reqBuilder.header(param.getKey(), parameterToString(param.getValue()));
383392
}
384393
for (Entry<String, String> header : defaultHeaderMap.entrySet()) {
385-
if (!headerParams.containsKey(header.getKey())) {
386-
reqBuilder.header(header.getKey(), parameterToString(header.getValue()));
387-
}
394+
if (!headerParams.containsKey(header.getKey())) {
395+
reqBuilder.header(header.getKey(), parameterToString(header.getValue()));
396+
}
397+
}
398+
if (extraHeaderParams != null) {
399+
for (Entry<String, String> header : extraHeaderParams.entrySet()) {
400+
reqBuilder.header(
401+
header.getKey(),
402+
parameterToString(header.getValue())
403+
);
404+
}
388405
}
389406
}
390407

0 commit comments

Comments
 (0)