Skip to content

Commit 0db5dd5

Browse files
feat: Support custom ObjectMappers, with singleton defaults (#844)
* Use static default object mappers, allow customization * Test default object mapper referential equality * chore: Update TwilioRestClient.java --------- Co-authored-by: Manisha Singh <singhmanisha.250025@gmail.com>
1 parent a6714ca commit 0db5dd5

File tree

4 files changed

+117
-36
lines changed

4 files changed

+117
-36
lines changed

src/main/java/com/twilio/http/TwilioRestClient.java

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,46 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
55
import com.twilio.auth_strategy.AuthStrategy;
6-
import com.twilio.auth_strategy.BasicAuthStrategy;
7-
import com.twilio.auth_strategy.TokenAuthStrategy;
86
import com.twilio.constant.EnumConstants;
9-
import com.twilio.credential.ClientCredentialProvider;
10-
import lombok.Getter;
11-
import org.slf4j.Logger;
12-
import org.slf4j.LoggerFactory;
13-
147
import java.util.ArrayList;
158
import java.util.List;
169
import java.util.Map;
1710
import java.util.function.Predicate;
11+
import lombok.Getter;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
1814

19-
15+
/**
16+
* The `TwilioRestClient` class is responsible for making HTTP requests to the Twilio API.
17+
* It provides methods to configure authentication, region, edge, and other settings
18+
* required to interact with Twilio's services.
19+
*
20+
* <p>Features:</p>
21+
* <ul>
22+
* <li>Supports basic authentication and token-based authentication strategies.</li>
23+
* <li>Allows configuration of custom HTTP clients and ObjectMapper for JSON processing.</li>
24+
* <li>Handles request retries for specific HTTP status codes (e.g., 401 Unauthorized).</li>
25+
* <li>Logs detailed request and response information for debugging purposes.</li>
26+
* </ul>
27+
*
28+
* <p>Usage Example:</p>
29+
* <pre>
30+
* {@code
31+
* TwilioRestClient client = new TwilioRestClient.Builder(ACCOUNT_SID, AUTH_TOKEN)
32+
* .objectMapper(customMapper)
33+
* .build();
34+
*
35+
* Message message = Message
36+
* .creator(
37+
* new PhoneNumber("+1xxxxxxxxxx"),
38+
* new PhoneNumber("+1xxxxxxxxxx"),
39+
* "This is the ship that made the Kessel Run in fourteen parsecs?"
40+
* ).create(client);
41+
* }
42+
* </pre>
43+
*
44+
* <p>Note: This class is designed to be thread-safe and reusable.</p>
45+
*/
2046
public class TwilioRestClient {
2147

2248
public static final int HTTP_STATUS_CODE_CREATED = 201;
@@ -51,14 +77,8 @@ protected TwilioRestClient(Builder b) {
5177
this.region = b.region;
5278
this.edge = b.edge;
5379
this.httpClient = b.httpClient;
54-
this.objectMapper = new ObjectMapper();
80+
this.objectMapper = b.objectMapper;
5581
this.userAgentExtensions = b.userAgentExtensions;
56-
57-
// This module configures the ObjectMapper to use
58-
// public API methods for manipulating java.time.*
59-
// classes. The alternative is to use reflection which
60-
// generates warnings from the module system on Java 9+
61-
objectMapper.registerModule(new JavaTimeModule());
6282
}
6383

6484
/**
@@ -71,7 +91,7 @@ public Response request(final Request request) {
7191
if (username != null && password != null) {
7292
request.setAuth(username, password);
7393
} else if (authStrategy != null) {
74-
request.setAuth(authStrategy);
94+
request.setAuth(authStrategy);
7595
}
7696

7797
if (region != null)
@@ -106,6 +126,13 @@ public Response request(final Request request) {
106126
}
107127

108128
public static class Builder {
129+
// This module configures the ObjectMapper to use
130+
// public API methods for manipulating java.time.*
131+
// classes. The alternative is to use reflection which
132+
// generates warnings from the module system on Java 9+
133+
private static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper()
134+
.registerModule(new JavaTimeModule());
135+
109136
private String username;
110137
private String password;
111138
private AuthStrategy authStrategy;
@@ -114,6 +141,7 @@ public static class Builder {
114141
private String edge;
115142
private HttpClient httpClient;
116143
private List<String> userAgentExtensions;
144+
private ObjectMapper objectMapper = DEFAULT_OBJECT_MAPPER;
117145

118146
/**
119147
* Create a new Twilio Rest Client.
@@ -163,6 +191,11 @@ public Builder userAgentExtensions(final List<String> userAgentExtensions) {
163191
return this;
164192
}
165193

194+
public Builder objectMapper(final ObjectMapper objectMapper) {
195+
this.objectMapper = objectMapper;
196+
return this;
197+
}
198+
166199
/**
167200
* Build new TwilioRestClient.
168201
*

src/main/java/com/twilio/http/bearertoken/BearerTokenTwilioRestClient.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,25 @@ private BearerTokenTwilioRestClient(BearerTokenTwilioRestClient.Builder b) {
4444
this.region = b.region;
4545
this.edge = b.edge;
4646
this.httpClient = b.httpClient;
47-
this.objectMapper = new ObjectMapper();
47+
this.objectMapper = b.objectMapper;
4848
this.userAgentExtensions = b.userAgentExtensions;
4949
this.tokenManager = b.tokenManager;
50+
}
5051

52+
public static class Builder {
5153
// This module configures the ObjectMapper to use
5254
// public API methods for manipulating java.time.*
5355
// classes. The alternative is to use reflection which
5456
// generates warnings from the module system on Java 9+
55-
objectMapper.registerModule(new JavaTimeModule());
56-
}
57-
58-
public static class Builder {
57+
private static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper()
58+
.registerModule(new JavaTimeModule());
59+
5960
private String region;
6061
private String edge;
6162
private BearerTokenHttpClient httpClient;
6263
private List<String> userAgentExtensions;
6364
private TokenManager tokenManager;
65+
private ObjectMapper objectMapper = DEFAULT_OBJECT_MAPPER;
6466

6567
public Builder() {
6668
this.region = System.getenv("TWILIO_REGION");
@@ -95,6 +97,11 @@ public BearerTokenTwilioRestClient.Builder userAgentExtensions(final List<String
9597
return this;
9698
}
9799

100+
public BearerTokenTwilioRestClient.Builder objectMapper(final ObjectMapper objectMapper) {
101+
this.objectMapper = objectMapper;
102+
return this;
103+
}
104+
98105
public BearerTokenTwilioRestClient build() {
99106
if (this.httpClient == null) {
100107
this.httpClient = new BearerTokenNetworkHttpClient();
@@ -111,7 +118,7 @@ public Response request(BearerTokenRequest request) {
111118
}
112119
}
113120
}
114-
121+
115122
request.setAuth(accessToken);
116123
if (region != null)
117124
request.setRegion(region);

src/main/java/com/twilio/http/noauth/NoAuthTwilioRestClient.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,23 @@ private NoAuthTwilioRestClient(NoAuthTwilioRestClient.Builder b) {
3838
this.region = b.region;
3939
this.edge = b.edge;
4040
this.httpClient = b.httpClient;
41-
this.objectMapper = new ObjectMapper();
41+
this.objectMapper = b.objectMapper;
4242
this.userAgentExtensions = b.userAgentExtensions;
43+
}
4344

45+
public static class Builder {
4446
// This module configures the ObjectMapper to use
4547
// public API methods for manipulating java.time.*
4648
// classes. The alternative is to use reflection which
4749
// generates warnings from the module system on Java 9+
48-
objectMapper.registerModule(new JavaTimeModule());
49-
}
50-
51-
public static class Builder {
50+
private static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper()
51+
.registerModule(new JavaTimeModule());
52+
5253
private String region;
5354
private String edge;
5455
private NoAuthNetworkHttpClient httpClient;
5556
private List<String> userAgentExtensions;
57+
private ObjectMapper objectMapper = DEFAULT_OBJECT_MAPPER;
5658

5759
public Builder() {
5860
this.region = System.getenv("TWILIO_REGION");
@@ -82,6 +84,11 @@ public NoAuthTwilioRestClient.Builder userAgentExtensions(final List<String> use
8284
return this;
8385
}
8486

87+
public NoAuthTwilioRestClient.Builder objectMapper(final ObjectMapper objectMapper) {
88+
this.objectMapper = objectMapper;
89+
return this;
90+
}
91+
8592
public NoAuthTwilioRestClient build() {
8693
if (this.httpClient == null) {
8794
this.httpClient = new NoAuthNetworkHttpClient();

src/test/java/com/twilio/http/TwilioRestClientTest.java

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
package com.twilio.http;
2-
import com.twilio.rest.Domains;
3-
import org.junit.Before;
4-
import org.junit.Test;
5-
import org.mockito.Mock;
6-
import org.mockito.MockitoAnnotations;
7-
8-
import java.util.Arrays;
9-
import java.util.Collections;
10-
import java.util.List;
112

123
import static org.junit.Assert.assertEquals;
134
import static org.junit.Assert.assertNotNull;
145
import static org.junit.Assert.assertNull;
6+
import static org.junit.Assert.assertTrue;
157
import static org.mockito.Mockito.when;
168

9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
11+
import com.twilio.rest.Domains;
12+
import java.util.Arrays;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import org.junit.Before;
16+
import org.junit.Test;
17+
import org.mockito.Mock;
18+
import org.mockito.MockitoAnnotations;
19+
1720
public class TwilioRestClientTest {
1821
private TwilioRestClient twilioRestClient;
1922
@Mock
@@ -48,6 +51,34 @@ public void testRequest() {
4851
assertNotNull(resp);
4952
}
5053

54+
@Test
55+
public void testRequestWithCustomObjectMapper() {
56+
Request request = new Request(
57+
HttpMethod.GET,
58+
Domains.API.toString(),
59+
URI
60+
);
61+
TwilioRestClient client = new TwilioRestClient.Builder(USER_NAME, TOKEN)
62+
.objectMapper(new ObjectMapper().registerModule(new JavaTimeModule()))
63+
.httpClient(httpClient)
64+
.build();
65+
66+
when(httpClient.reliableRequest(request)).thenReturn(new Response("", 200));
67+
68+
Response resp = client.request(request);
69+
assertNotNull(resp);
70+
}
71+
72+
@Test
73+
public void testUsesSingletonDefaultObjectMapper() {
74+
TwilioRestClient client1 = new TwilioRestClient.Builder(USER_NAME, TOKEN)
75+
.build();
76+
TwilioRestClient client2 = new TwilioRestClient.Builder(USER_NAME, TOKEN)
77+
.build();
78+
79+
assertTrue(client1.getObjectMapper() == client2.getObjectMapper());
80+
}
81+
5182
@Test
5283
public void testRequestWithExtension() {
5384
Request request = new Request(
@@ -57,6 +88,7 @@ public void testRequestWithExtension() {
5788
);
5889
twilioRestClientExtension = new TwilioRestClient.Builder(USER_NAME, TOKEN)
5990
.userAgentExtensions(userAgentStringExtensions)
91+
.httpClient(httpClient)
6092
.build();
6193
twilioRestClientExtension.request(request);
6294
assertEquals(userAgentStringExtensions, request.getUserAgentExtensions());
@@ -71,6 +103,7 @@ public void testRequestWithExtensionEmpty() {
71103
);
72104
twilioRestClientExtension = new TwilioRestClient.Builder(USER_NAME, TOKEN)
73105
.userAgentExtensions(Collections.emptyList())
106+
.httpClient(httpClient)
74107
.build();
75108
twilioRestClientExtension.request(request);
76109
assertNull(request.getUserAgentExtensions());
@@ -85,6 +118,7 @@ public void testRequestWithExtensionNull() {
85118
);
86119
twilioRestClientExtension = new TwilioRestClient.Builder(USER_NAME, TOKEN)
87120
.userAgentExtensions(null)
121+
.httpClient(httpClient)
88122
.build();
89123
twilioRestClientExtension.request(request);
90124
assertNull(request.getUserAgentExtensions());

0 commit comments

Comments
 (0)