Skip to content

Commit b229806

Browse files
arriolacJustin Poehnelt
andauthored
feat: add support for experience id. (#647)
* feat: add support for experience id * Adding setter and getter for experience ID. * Reformat code based on google-java-format * Adding tests. * Fix formatting. * Adding sample tags. * Fix formatting. Co-authored-by: Justin Poehnelt <jpoehnelt@google.com>
1 parent cd6c56c commit b229806

5 files changed

Lines changed: 197 additions & 10 deletions

File tree

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
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;
3132
import java.net.MalformedURLException;
3233
import java.net.Proxy;
3334
import java.net.URL;
@@ -51,6 +52,7 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handle(
5152
String hostName,
5253
String url,
5354
String userAgent,
55+
String experienceIdHeaderValue,
5456
Class<R> clazz,
5557
FieldNamingPolicy fieldNamingPolicy,
5658
long errorTimeout,
@@ -60,6 +62,10 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handle(
6062
HTTPRequest req;
6163
try {
6264
req = new HTTPRequest(new URL(hostName + url), HTTPMethod.POST, fetchOptions);
65+
if (experienceIdHeaderValue != null) {
66+
req.setHeader(
67+
new HTTPHeader(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID, experienceIdHeaderValue));
68+
}
6369
} catch (MalformedURLException e) {
6470
LOG.error("Request: {}{}", hostName, url, e);
6571
throw (new RuntimeException(e));
@@ -75,6 +81,7 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handlePost(
7581
String url,
7682
String payload,
7783
String userAgent,
84+
String experienceIdHeaderValue,
7885
Class<R> clazz,
7986
FieldNamingPolicy fieldNamingPolicy,
8087
long errorTimeout,
@@ -85,6 +92,10 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handlePost(
8592
try {
8693
req = new HTTPRequest(new URL(hostName + url), HTTPMethod.POST, fetchOptions);
8794
req.setHeader(new HTTPHeader("Content-Type", "application/json; charset=utf-8"));
95+
if (experienceIdHeaderValue != null) {
96+
req.setHeader(
97+
new HTTPHeader(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID, experienceIdHeaderValue));
98+
}
8899
req.setPayload(payload.getBytes(UTF_8));
89100
} catch (MalformedURLException e) {
90101
LOG.error("Request: {}{}", hostName, url, e);

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

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.google.maps.internal.ApiConfig;
2222
import com.google.maps.internal.ApiResponse;
2323
import com.google.maps.internal.ExceptionsAllowedToRetry;
24+
import com.google.maps.internal.HttpHeaders;
25+
import com.google.maps.internal.StringJoin;
2426
import com.google.maps.internal.UrlSigner;
2527
import java.io.UnsupportedEncodingException;
2628
import java.net.Proxy;
@@ -61,6 +63,7 @@ public class GeoApiContext {
6163
private final ExceptionsAllowedToRetry exceptionsAllowedToRetry;
6264
private final Integer maxRetries;
6365
private final UrlSigner urlSigner;
66+
private String experienceIdHeaderValue;
6467

6568
/* package */
6669
GeoApiContext(
@@ -72,7 +75,8 @@ public class GeoApiContext {
7275
long errorTimeout,
7376
ExceptionsAllowedToRetry exceptionsAllowedToRetry,
7477
Integer maxRetries,
75-
UrlSigner urlSigner) {
78+
UrlSigner urlSigner,
79+
String... experienceIdHeaderValue) {
7680
this.requestHandler = requestHandler;
7781
this.apiKey = apiKey;
7882
this.baseUrlOverride = baseUrlOverride;
@@ -82,6 +86,7 @@ public class GeoApiContext {
8286
this.exceptionsAllowedToRetry = exceptionsAllowedToRetry;
8387
this.maxRetries = maxRetries;
8488
this.urlSigner = urlSigner;
89+
setExperienceId(experienceIdHeaderValue);
8590
}
8691

8792
/**
@@ -98,6 +103,7 @@ <T, R extends ApiResponse<T>> PendingResult<T> handle(
98103
String hostName,
99104
String url,
100105
String userAgent,
106+
String experienceIdHeaderValue,
101107
Class<R> clazz,
102108
FieldNamingPolicy fieldNamingPolicy,
103109
long errorTimeout,
@@ -109,6 +115,7 @@ <T, R extends ApiResponse<T>> PendingResult<T> handlePost(
109115
String url,
110116
String payload,
111117
String userAgent,
118+
String experienceIdHeaderValue,
112119
Class<R> clazz,
113120
FieldNamingPolicy fieldNamingPolicy,
114121
long errorTimeout,
@@ -136,6 +143,34 @@ interface Builder {
136143
}
137144
}
138145

146+
/**
147+
* Sets the value for the HTTP header field name {@link HttpHeaders#X_GOOG_MAPS_EXPERIENCE_ID} to
148+
* be used on subsequent API calls. Calling this method with {@code null} is equivalent to calling
149+
* {@link #clearExperienceId()}.
150+
*
151+
* @param experienceId The experience ID if set, otherwise null
152+
*/
153+
public void setExperienceId(String... experienceId) {
154+
if (experienceId == null || experienceId.length == 0) {
155+
experienceIdHeaderValue = null;
156+
return;
157+
}
158+
experienceIdHeaderValue = StringJoin.join(",", experienceId);
159+
}
160+
161+
/** @return Returns the experience ID if set, otherwise, null */
162+
public String getExperienceId() {
163+
return experienceIdHeaderValue;
164+
}
165+
166+
/**
167+
* Clears the experience ID if set the HTTP header field {@link
168+
* HttpHeaders#X_GOOG_MAPS_EXPERIENCE_ID} will be omitted from subsequent calls.
169+
*/
170+
public void clearExperienceId() {
171+
experienceIdHeaderValue = null;
172+
}
173+
139174
/**
140175
* Shut down this GeoApiContext instance, reclaiming resources. After shutdown() has been called,
141176
* no further queries may be done against this instance.
@@ -240,6 +275,7 @@ <T, R extends ApiResponse<T>> PendingResult<T> post(
240275
url.toString(),
241276
params.get("_payload").get(0),
242277
USER_AGENT,
278+
experienceIdHeaderValue,
243279
clazz,
244280
config.fieldNamingPolicy,
245281
errorTimeout,
@@ -280,6 +316,7 @@ private <T, R extends ApiResponse<T>> PendingResult<T> getWithPath(
280316
hostName,
281317
url.toString(),
282318
USER_AGENT,
319+
experienceIdHeaderValue,
283320
clazz,
284321
fieldNamingPolicy,
285322
errorTimeout,
@@ -312,6 +349,7 @@ public static class Builder {
312349
private ExceptionsAllowedToRetry exceptionsAllowedToRetry = new ExceptionsAllowedToRetry();
313350
private Integer maxRetries;
314351
private UrlSigner urlSigner;
352+
private String[] experienceIdHeaderValue;
315353

316354
/** Builder pattern for the enclosing {@code GeoApiContext}. */
317355
public Builder() {
@@ -533,6 +571,18 @@ public Builder proxyAuthentication(String proxyUserName, String proxyUserPasswor
533571
return this;
534572
}
535573

574+
/**
575+
* Sets the value for the HTTP header field name {@link HttpHeaders#X_GOOG_MAPS_EXPERIENCE_ID}
576+
* HTTP header value for the field name on subsequent API calls.
577+
*
578+
* @param experienceId The experience ID
579+
* @return Returns this builder for call chaining.
580+
*/
581+
public Builder experienceId(String... experienceId) {
582+
this.experienceIdHeaderValue = experienceId;
583+
return this;
584+
}
585+
536586
/**
537587
* Converts this builder into a {@code GeoApiContext}.
538588
*
@@ -548,7 +598,8 @@ public GeoApiContext build() {
548598
errorTimeout,
549599
exceptionsAllowedToRetry,
550600
maxRetries,
551-
urlSigner);
601+
urlSigner,
602+
experienceIdHeaderValue);
552603
}
553604
}
554605
}

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.maps.GeoApiContext.RequestHandler;
2020
import com.google.maps.internal.ApiResponse;
2121
import com.google.maps.internal.ExceptionsAllowedToRetry;
22+
import com.google.maps.internal.HttpHeaders;
2223
import com.google.maps.internal.OkHttpPendingResult;
2324
import com.google.maps.internal.RateLimitExecutorService;
2425
import java.io.IOException;
@@ -55,13 +56,17 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handle(
5556
String hostName,
5657
String url,
5758
String userAgent,
59+
String experienceIdHeaderValue,
5860
Class<R> clazz,
5961
FieldNamingPolicy fieldNamingPolicy,
6062
long errorTimeout,
6163
Integer maxRetries,
6264
ExceptionsAllowedToRetry exceptionsAllowedToRetry) {
63-
Request req =
64-
new Request.Builder().get().header("User-Agent", userAgent).url(hostName + url).build();
65+
Request.Builder builder = new Request.Builder().get().header("User-Agent", userAgent);
66+
if (experienceIdHeaderValue != null) {
67+
builder = builder.header(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID, experienceIdHeaderValue);
68+
}
69+
Request req = builder.url(hostName + url).build();
6570

6671
return new OkHttpPendingResult<>(
6772
req, client, clazz, fieldNamingPolicy, errorTimeout, maxRetries, exceptionsAllowedToRetry);
@@ -73,18 +78,19 @@ public <T, R extends ApiResponse<T>> PendingResult<T> handlePost(
7378
String url,
7479
String payload,
7580
String userAgent,
81+
String experienceIdHeaderValue,
7682
Class<R> clazz,
7783
FieldNamingPolicy fieldNamingPolicy,
7884
long errorTimeout,
7985
Integer maxRetries,
8086
ExceptionsAllowedToRetry exceptionsAllowedToRetry) {
8187
RequestBody body = RequestBody.create(JSON, payload);
82-
Request req =
83-
new Request.Builder()
84-
.post(body)
85-
.header("User-Agent", userAgent)
86-
.url(hostName + url)
87-
.build();
88+
Request.Builder builder = new Request.Builder().post(body).header("User-Agent", userAgent);
89+
90+
if (experienceIdHeaderValue != null) {
91+
builder = builder.header(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID, experienceIdHeaderValue);
92+
}
93+
Request req = builder.url(hostName + url).build();
8894

8995
return new OkHttpPendingResult<>(
9096
req, client, clazz, fieldNamingPolicy, errorTimeout, maxRetries, exceptionsAllowedToRetry);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2020 Google Inc. All rights reserved.
3+
*
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6+
* file except in compliance with the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software distributed under
11+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12+
* ANY KIND, either express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.google.maps.internal;
17+
18+
/** Contains HTTP header name constants. */
19+
public final class HttpHeaders {
20+
21+
/** The HTTP {@code X-Goog-Maps-Experience-ID} header field name. */
22+
public static final String X_GOOG_MAPS_EXPERIENCE_ID = "X-Goog-Maps-Experience-ID";
23+
}

src/test/java/com/google/maps/GeoApiContextTest.java

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@
1919
import static org.junit.Assert.assertEquals;
2020
import static org.junit.Assert.assertFalse;
2121
import static org.junit.Assert.assertNotNull;
22+
import static org.junit.Assert.assertNull;
2223
import static org.junit.Assert.assertTrue;
2324
import static org.junit.Assert.fail;
2425
import static org.mockito.Mockito.mock;
2526

2627
import com.google.maps.errors.OverQueryLimitException;
2728
import com.google.maps.internal.ApiConfig;
2829
import com.google.maps.internal.ApiResponse;
30+
import com.google.maps.internal.HttpHeaders;
2931
import com.google.maps.model.GeocodingResult;
3032
import java.io.IOException;
3133
import java.util.Collections;
3234
import java.util.HashMap;
3335
import java.util.List;
3436
import java.util.Map;
37+
import java.util.UUID;
3538
import java.util.concurrent.TimeUnit;
3639
import okhttp3.Headers;
3740
import okhttp3.mockwebserver.MockResponse;
@@ -299,6 +302,99 @@ public void testToggleIfExceptionIsAllowedToRetry() throws Exception {
299302
fail("OverQueryLimitException was expected but not observed.");
300303
}
301304

305+
@Test
306+
public void testSingleExperienceId() {
307+
final String experienceId = "experienceId";
308+
final GeoApiContext context = builder.experienceId(experienceId).build();
309+
assertEquals(experienceId, context.getExperienceId());
310+
}
311+
312+
@Test
313+
public void testMultipleExperienceId() {
314+
final String experienceId1 = "experienceId1";
315+
final String experienceId2 = "experienceId2";
316+
final GeoApiContext context = builder.experienceId(experienceId1, experienceId2).build();
317+
assertEquals(experienceId1 + "," + experienceId2, context.getExperienceId());
318+
}
319+
320+
@Test
321+
public void testNoExperienceId() {
322+
final GeoApiContext context = builder.build();
323+
assertNull(context.getExperienceId());
324+
}
325+
326+
@Test
327+
public void testClearingExperienceId() {
328+
final String experienceId = "experienceId";
329+
final GeoApiContext context = builder.experienceId(experienceId).build();
330+
assertEquals(experienceId, context.getExperienceId());
331+
332+
context.clearExperienceId();
333+
assertNull(context.getExperienceId());
334+
}
335+
336+
@Test
337+
public void testExperienceIdIsInHeader() throws Exception {
338+
final String experienceId = "exp1";
339+
final RecordedRequest request = makeMockRequest(experienceId);
340+
assertEquals(experienceId, request.getHeader(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID));
341+
}
342+
343+
@Test
344+
public void testExperienceIdNotInHeader() throws Exception {
345+
final RecordedRequest request = makeMockRequest();
346+
final String value = request.getHeader(HttpHeaders.X_GOOG_MAPS_EXPERIENCE_ID);
347+
assertNull(value);
348+
}
349+
350+
@Test
351+
public void testExperienceIdSample() {
352+
// [START maps_experience_id]
353+
final String experienceId = UUID.randomUUID().toString();
354+
355+
// instantiate context with experience id
356+
final GeoApiContext context =
357+
new GeoApiContext.Builder().apiKey("AIza-Maps-API-Key").experienceId(experienceId).build();
358+
359+
// clear the current experience id
360+
context.clearExperienceId();
361+
362+
// set a new experience id
363+
final String otherExperienceId = UUID.randomUUID().toString();
364+
context.setExperienceId(experienceId, otherExperienceId);
365+
366+
// make API request, the client will set the header
367+
// X-GOOG-MAPS-EXPERIENCE-ID: experienceId,otherExperienceId
368+
369+
// get current experience id
370+
final String ids = context.getExperienceId();
371+
// [END maps_experience_id]
372+
373+
assertEquals(experienceId + "," + otherExperienceId, ids);
374+
}
375+
376+
@SuppressWarnings("unchecked")
377+
private RecordedRequest makeMockRequest(String... experienceId) throws Exception {
378+
// Set up a mock request
379+
ApiResponse<Object> fakeResponse = mock(ApiResponse.class);
380+
String path = "/";
381+
Map<String, List<String>> params = new HashMap<>();
382+
params.put("key", Collections.singletonList("value"));
383+
384+
// Set up the fake web server
385+
server.enqueue(new MockResponse());
386+
server.start();
387+
setMockBaseUrl();
388+
389+
// Build & execute the request using our context
390+
final GeoApiContext context = builder.experienceId(experienceId).build();
391+
context.get(new ApiConfig(path), fakeResponse.getClass(), params).awaitIgnoreError();
392+
393+
// Read the header
394+
server.shutdown();
395+
return server.takeRequest();
396+
}
397+
302398
@Test
303399
public void testShutdown() throws InterruptedException {
304400
GeoApiContext context = builder.build();

0 commit comments

Comments
 (0)