Skip to content

Commit 325b67e

Browse files
authored
feat!: Support setting experienceIds on a per request basis. (#744)
BREAKING CHANGE: Setting an experience ID is now done on a per-request basis vs. setting it on the GeoApiContext object.
1 parent 3ce30f1 commit 325b67e

File tree

8 files changed

+201
-155
lines changed

8 files changed

+201
-155
lines changed

src/main/java/com/google/maps/GaeRequestHandler.java

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
import com.google.maps.internal.ApiResponse;
2929
import com.google.maps.internal.ExceptionsAllowedToRetry;
3030
import com.google.maps.internal.GaePendingResult;
31-
import com.google.maps.internal.HttpHeaders;
3231
import com.google.maps.metrics.RequestMetrics;
3332
import java.net.MalformedURLException;
3433
import java.net.Proxy;
3534
import java.net.URL;
35+
import java.util.Map;
3636
import java.util.concurrent.TimeUnit;
3737
import org.slf4j.Logger;
3838
import org.slf4j.LoggerFactory;
@@ -52,22 +52,21 @@ public class GaeRequestHandler implements GeoApiContext.RequestHandler {
5252
public <T, R extends ApiResponse<T>> PendingResult<T> handle(
5353
String hostName,
5454
String url,
55-
String userAgent,
56-
String experienceIdHeaderValue,
55+
Map<String, String> headers,
5756
Class<R> clazz,
5857
FieldNamingPolicy fieldNamingPolicy,
5958
long errorTimeout,
6059
Integer maxRetries,
6160
ExceptionsAllowedToRetry exceptionsAllowedToRetry,
6261
RequestMetrics metrics) {
6362
FetchOptions fetchOptions = FetchOptions.Builder.withDeadline(10);
64-
HTTPRequest req;
63+
final HTTPRequest req;
6564
try {
6665
req = new HTTPRequest(new URL(hostName + url), HTTPMethod.POST, fetchOptions);
67-
if (experienceIdHeaderValue != null) {
68-
req.setHeader(
69-
new HTTPHeader(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID, experienceIdHeaderValue));
70-
}
66+
headers.forEach(
67+
(k, v) -> {
68+
req.addHeader(new HTTPHeader(k, v));
69+
});
7170
} catch (MalformedURLException e) {
7271
LOG.error("Request: {}{}", hostName, url, e);
7372
throw (new RuntimeException(e));
@@ -89,23 +88,22 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handlePost(
8988
String hostName,
9089
String url,
9190
String payload,
92-
String userAgent,
93-
String experienceIdHeaderValue,
91+
Map<String, String> headers,
9492
Class<R> clazz,
9593
FieldNamingPolicy fieldNamingPolicy,
9694
long errorTimeout,
9795
Integer maxRetries,
9896
ExceptionsAllowedToRetry exceptionsAllowedToRetry,
9997
RequestMetrics metrics) {
10098
FetchOptions fetchOptions = FetchOptions.Builder.withDeadline(10);
101-
HTTPRequest req = null;
99+
final HTTPRequest req;
102100
try {
103101
req = new HTTPRequest(new URL(hostName + url), HTTPMethod.POST, fetchOptions);
104102
req.setHeader(new HTTPHeader("Content-Type", "application/json; charset=utf-8"));
105-
if (experienceIdHeaderValue != null) {
106-
req.setHeader(
107-
new HTTPHeader(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID, experienceIdHeaderValue));
108-
}
103+
headers.forEach(
104+
(k, v) -> {
105+
req.addHeader(new HTTPHeader(k, v));
106+
});
109107
req.setPayload(payload.getBytes(UTF_8));
110108
} catch (MalformedURLException e) {
111109
LOG.error("Request: {}{}", hostName, url, e);

src/main/java/com/google/maps/GeoApiContext.java

Lines changed: 57 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import com.google.maps.internal.ApiResponse;
2323
import com.google.maps.internal.ExceptionsAllowedToRetry;
2424
import com.google.maps.internal.HttpHeaders;
25-
import com.google.maps.internal.StringJoin;
2625
import com.google.maps.internal.UrlSigner;
2726
import com.google.maps.metrics.NoOpRequestMetricsReporter;
2827
import com.google.maps.metrics.RequestMetrics;
@@ -35,8 +34,10 @@
3534
import java.security.InvalidKeyException;
3635
import java.security.NoSuchAlgorithmException;
3736
import java.util.Collections;
37+
import java.util.HashMap;
3838
import java.util.List;
3939
import java.util.Map;
40+
import java.util.Map.Entry;
4041
import java.util.concurrent.TimeUnit;
4142

4243
/**
@@ -68,8 +69,8 @@ public class GeoApiContext implements Closeable {
6869
private final ExceptionsAllowedToRetry exceptionsAllowedToRetry;
6970
private final Integer maxRetries;
7071
private final UrlSigner urlSigner;
71-
private String experienceIdHeaderValue;
7272
private final RequestMetricsReporter requestMetricsReporter;
73+
private final Map<String, String> defaultHeaders = new HashMap<>();
7374

7475
/* package */
7576
GeoApiContext(
@@ -82,8 +83,7 @@ public class GeoApiContext implements Closeable {
8283
ExceptionsAllowedToRetry exceptionsAllowedToRetry,
8384
Integer maxRetries,
8485
UrlSigner urlSigner,
85-
RequestMetricsReporter requestMetricsReporter,
86-
String... experienceIdHeaderValue) {
86+
RequestMetricsReporter requestMetricsReporter) {
8787
this.requestHandler = requestHandler;
8888
this.apiKey = apiKey;
8989
this.baseUrlOverride = baseUrlOverride;
@@ -94,7 +94,7 @@ public class GeoApiContext implements Closeable {
9494
this.maxRetries = maxRetries;
9595
this.urlSigner = urlSigner;
9696
this.requestMetricsReporter = requestMetricsReporter;
97-
setExperienceId(experienceIdHeaderValue);
97+
defaultHeaders.put(HttpHeaders.USER_AGENT, USER_AGENT);
9898
}
9999

100100
/**
@@ -120,8 +120,7 @@ public interface RequestHandler {
120120
<T, R extends ApiResponse<T>> PendingResult<T> handle(
121121
String hostName,
122122
String url,
123-
String userAgent,
124-
String experienceIdHeaderValue,
123+
Map<String, String> headers,
125124
Class<R> clazz,
126125
FieldNamingPolicy fieldNamingPolicy,
127126
long errorTimeout,
@@ -133,8 +132,7 @@ <T, R extends ApiResponse<T>> PendingResult<T> handlePost(
133132
String hostName,
134133
String url,
135134
String payload,
136-
String userAgent,
137-
String experienceIdHeaderValue,
135+
Map<String, String> headers,
138136
Class<R> clazz,
139137
FieldNamingPolicy fieldNamingPolicy,
140138
long errorTimeout,
@@ -163,34 +161,6 @@ interface Builder {
163161
}
164162
}
165163

166-
/**
167-
* Sets the value for the HTTP header field name {@link HttpHeaders#X_GOOG_MAPS_EXPERIENCE_ID} to
168-
* be used on subsequent API calls. Calling this method with {@code null} is equivalent to calling
169-
* {@link #clearExperienceId()}.
170-
*
171-
* @param experienceId The experience ID if set, otherwise null
172-
*/
173-
public void setExperienceId(String... experienceId) {
174-
if (experienceId == null || experienceId.length == 0) {
175-
experienceIdHeaderValue = null;
176-
return;
177-
}
178-
experienceIdHeaderValue = StringJoin.join(",", experienceId);
179-
}
180-
181-
/** @return Returns the experience ID if set, otherwise, null */
182-
public String getExperienceId() {
183-
return experienceIdHeaderValue;
184-
}
185-
186-
/**
187-
* Clears the experience ID if set the HTTP header field {@link
188-
* HttpHeaders#X_GOOG_MAPS_EXPERIENCE_ID} will be omitted from subsequent calls.
189-
*/
190-
public void clearExperienceId() {
191-
experienceIdHeaderValue = null;
192-
}
193-
194164
/**
195165
* Shut down this GeoApiContext instance, reclaiming resources. After shutdown() has been called,
196166
* no further queries may be done against this instance.
@@ -199,8 +169,21 @@ public void shutdown() {
199169
requestHandler.shutdown();
200170
}
201171

172+
private Map<String, String> addDefaultHeaders(Map<String, String> headers) {
173+
Map<String, String> newHeaders = new HashMap<>(headers);
174+
for (Entry<String, String> entry : defaultHeaders.entrySet()) {
175+
if (!newHeaders.containsKey(entry.getKey())) {
176+
newHeaders.put(entry.getKey(), entry.getValue());
177+
}
178+
}
179+
return newHeaders;
180+
}
181+
202182
<T, R extends ApiResponse<T>> PendingResult<T> get(
203-
ApiConfig config, Class<? extends R> clazz, Map<String, List<String>> params) {
183+
ApiConfig config,
184+
Class<? extends R> clazz,
185+
Map<String, String> headers,
186+
Map<String, List<String>> params) {
204187
if (channel != null && !channel.isEmpty() && !params.containsKey("channel")) {
205188
params.put("channel", Collections.singletonList(channel));
206189
}
@@ -227,11 +210,18 @@ <T, R extends ApiResponse<T>> PendingResult<T> get(
227210
config.path,
228211
config.supportsClientId,
229212
query.toString(),
230-
requestMetricsReporter.newRequest(config.path));
213+
requestMetricsReporter.newRequest(config.path),
214+
headers);
231215
}
232216

233217
<T, R extends ApiResponse<T>> PendingResult<T> get(
234-
ApiConfig config, Class<? extends R> clazz, String... params) {
218+
ApiConfig config, Class<? extends R> clazz, Map<String, List<String>> params) {
219+
return get(config, clazz, Collections.emptyMap(), params);
220+
}
221+
222+
<T, R extends ApiResponse<T>> PendingResult<T> get(
223+
ApiConfig config, Class<? extends R> clazz, Map<String, String> headers, String... params) {
224+
// FIXME: Refactor this to reuse implementation in overloaded get()
235225
if (params.length % 2 != 0) {
236226
throw new IllegalArgumentException("Params must be matching key/value pairs.");
237227
}
@@ -260,19 +250,29 @@ <T, R extends ApiResponse<T>> PendingResult<T> get(
260250
query.append("&channel=").append(channel);
261251
}
262252

253+
final Map<String, String> allHeaders = addDefaultHeaders(headers);
254+
263255
return getWithPath(
264256
clazz,
265257
config.fieldNamingPolicy,
266258
config.hostName,
267259
config.path,
268260
config.supportsClientId,
269261
query.toString(),
270-
requestMetricsReporter.newRequest(config.path));
262+
requestMetricsReporter.newRequest(config.path),
263+
allHeaders);
271264
}
272265

273-
<T, R extends ApiResponse<T>> PendingResult<T> post(
274-
ApiConfig config, Class<? extends R> clazz, Map<String, List<String>> params) {
266+
<T, R extends ApiResponse<T>> PendingResult<T> get(
267+
ApiConfig config, Class<? extends R> clazz, String... params) {
268+
return get(config, clazz, Collections.emptyMap(), params);
269+
}
275270

271+
<T, R extends ApiResponse<T>> PendingResult<T> post(
272+
ApiConfig config,
273+
Class<? extends R> clazz,
274+
Map<String, String> headers,
275+
Map<String, List<String>> params) {
276276
checkContext(config.supportsClientId);
277277

278278
StringBuilder url = new StringBuilder(config.path);
@@ -292,12 +292,13 @@ <T, R extends ApiResponse<T>> PendingResult<T> post(
292292
hostName = baseUrlOverride;
293293
}
294294

295+
final Map<String, String> allHeaders = addDefaultHeaders(headers);
296+
295297
return requestHandler.handlePost(
296298
hostName,
297299
url.toString(),
298300
params.get("_payload").get(0),
299-
USER_AGENT,
300-
experienceIdHeaderValue,
301+
allHeaders,
301302
clazz,
302303
config.fieldNamingPolicy,
303304
errorTimeout,
@@ -306,14 +307,20 @@ <T, R extends ApiResponse<T>> PendingResult<T> post(
306307
requestMetricsReporter.newRequest(config.path));
307308
}
308309

310+
<T, R extends ApiResponse<T>> PendingResult<T> post(
311+
ApiConfig config, Class<? extends R> clazz, Map<String, List<String>> params) {
312+
return post(config, clazz, Collections.emptyMap(), params);
313+
}
314+
309315
private <T, R extends ApiResponse<T>> PendingResult<T> getWithPath(
310316
Class<R> clazz,
311317
FieldNamingPolicy fieldNamingPolicy,
312318
String hostName,
313319
String path,
314320
boolean canUseClientId,
315321
String encodedPath,
316-
RequestMetrics metrics) {
322+
RequestMetrics metrics,
323+
Map<String, String> headers) {
317324
checkContext(canUseClientId);
318325
if (!encodedPath.startsWith("&")) {
319326
throw new IllegalArgumentException("encodedPath must start with &");
@@ -336,11 +343,12 @@ private <T, R extends ApiResponse<T>> PendingResult<T> getWithPath(
336343
hostName = baseUrlOverride;
337344
}
338345

346+
final Map<String, String> allHeaders = addDefaultHeaders(headers);
347+
339348
return requestHandler.handle(
340349
hostName,
341350
url.toString(),
342-
USER_AGENT,
343-
experienceIdHeaderValue,
351+
allHeaders,
344352
clazz,
345353
fieldNamingPolicy,
346354
errorTimeout,
@@ -375,7 +383,6 @@ public static class Builder {
375383
private Integer maxRetries;
376384
private UrlSigner urlSigner;
377385
private RequestMetricsReporter requestMetricsReporter = new NoOpRequestMetricsReporter();
378-
private String[] experienceIdHeaderValue;
379386

380387
/** Builder pattern for the enclosing {@code GeoApiContext}. */
381388
public Builder() {
@@ -598,18 +605,6 @@ public Builder proxyAuthentication(String proxyUserName, String proxyUserPasswor
598605
return this;
599606
}
600607

601-
/**
602-
* Sets the value for the HTTP header field name {@link HttpHeaders#X_GOOG_MAPS_EXPERIENCE_ID}
603-
* HTTP header value for the field name on subsequent API calls.
604-
*
605-
* @param experienceId The experience ID
606-
* @return Returns this builder for call chaining.
607-
*/
608-
public Builder experienceId(String... experienceId) {
609-
this.experienceIdHeaderValue = experienceId;
610-
return this;
611-
}
612-
613608
public Builder requestMetricsReporter(RequestMetricsReporter requestMetricsReporter) {
614609
this.requestMetricsReporter = requestMetricsReporter;
615610
return this;
@@ -631,8 +626,7 @@ public GeoApiContext build() {
631626
exceptionsAllowedToRetry,
632627
maxRetries,
633628
urlSigner,
634-
requestMetricsReporter,
635-
experienceIdHeaderValue);
629+
requestMetricsReporter);
636630
}
637631
}
638632
}

0 commit comments

Comments
 (0)