Skip to content

Commit ac3b3ba

Browse files
committed
Modernize StripeResponse (#932)
1 parent 5bbe156 commit ac3b3ba

File tree

5 files changed

+124
-78
lines changed

5 files changed

+124
-78
lines changed

Diff for: src/main/java/com/stripe/net/HttpClient.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public StripeResponse requestWithRetries(StripeRequest request) throws StripeExc
114114
throw requestException;
115115
}
116116

117-
response.setNumRetries(retry);
117+
response.numRetries(retry);
118118

119119
return response;
120120
}

Diff for: src/main/java/com/stripe/net/HttpURLConnectionClient.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,16 @@ public StripeResponse request(StripeRequest request) throws ApiConnectionExcepti
5050

5151
// trigger the request
5252
int responseCode = conn.getResponseCode();
53+
HttpHeaders headers = HttpHeaders.of(conn.getHeaderFields());
5354
String responseBody;
54-
Map<String, List<String>> headers;
5555

5656
if (responseCode >= 200 && responseCode < 300) {
5757
responseBody = getResponseBody(conn.getInputStream());
5858
} else {
5959
responseBody = getResponseBody(conn.getErrorStream());
6060
}
6161

62-
headers = conn.getHeaderFields();
63-
64-
return new StripeResponse(responseCode, responseBody, headers);
62+
return new StripeResponse(responseCode, headers, responseBody);
6563

6664
} catch (IOException e) {
6765
throw new ApiConnectionException(

Diff for: src/main/java/com/stripe/net/StripeResponse.java

+61-33
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,82 @@
11
package com.stripe.net;
22

3-
import java.util.List;
4-
import java.util.Map;
3+
import static java.util.Objects.requireNonNull;
54

6-
public class StripeResponse {
5+
import java.time.Instant;
6+
import java.time.ZonedDateTime;
7+
import java.time.format.DateTimeFormatter;
8+
import java.util.Optional;
9+
import lombok.AccessLevel;
10+
import lombok.Getter;
11+
import lombok.Setter;
12+
import lombok.Value;
13+
import lombok.experimental.Accessors;
14+
import lombok.experimental.NonFinal;
715

16+
/** A response from Stripe's API. */
17+
@Value
18+
@Accessors(fluent = true)
19+
public class StripeResponse {
20+
/** The HTTP status code of the response. */
821
int code;
9-
String body;
22+
23+
/** The HTTP headers of the response. */
1024
HttpHeaders headers;
25+
26+
/** The body of the response. */
27+
String body;
28+
29+
/** Number of times the request was retried. Used for internal tests only. */
30+
@NonFinal
31+
@Getter(AccessLevel.PACKAGE)
32+
@Setter(AccessLevel.PACKAGE)
1133
int numRetries;
1234

13-
/** Constructs a Stripe response with the specified status code and body. */
14-
public StripeResponse(int code, String body) {
15-
this.code = code;
16-
this.body = body;
17-
this.headers = null;
18-
}
35+
/**
36+
* Initializes a new instance of the {@link StripeResponse} class.
37+
*
38+
* @param code the HTTP status code of the response
39+
* @param headers the HTTP headers of the response
40+
* @param body the body of the response
41+
* @throws NullPointerException if {@code headers} or {@code body} is {@code null}
42+
*/
43+
public StripeResponse(int code, HttpHeaders headers, String body) {
44+
requireNonNull(headers);
45+
requireNonNull(body);
1946

20-
/** Constructs a Stripe response with the specified status code, body and headers. */
21-
public StripeResponse(int code, String body, Map<String, List<String>> headers) {
2247
this.code = code;
48+
this.headers = headers;
2349
this.body = body;
24-
this.headers = HttpHeaders.of(headers);
25-
}
26-
27-
public int code() {
28-
return this.code;
29-
}
30-
31-
public String body() {
32-
return this.body;
3350
}
3451

35-
public HttpHeaders headers() {
36-
return headers;
52+
/**
53+
* Gets the date of the request, as returned by Stripe.
54+
*
55+
* @return the date of the request, as returned by Stripe
56+
*/
57+
public Instant date() {
58+
Optional<String> dateStr = this.headers.firstValue("Date");
59+
if (!dateStr.isPresent()) {
60+
return null;
61+
}
62+
return ZonedDateTime.parse(dateStr.get(), DateTimeFormatter.RFC_1123_DATE_TIME).toInstant();
3763
}
3864

65+
/**
66+
* Gets the idempotency key of the request, as returned by Stripe.
67+
*
68+
* @return the idempotency key of the request, as returned by Stripe
69+
*/
3970
public String idempotencyKey() {
40-
return (headers != null) ? headers.firstValue("Idempotency-Key").orElse(null) : null;
71+
return this.headers.firstValue("Idempotency-Key").orElse(null);
4172
}
4273

74+
/**
75+
* Gets the ID of the request, as returned by Stripe.
76+
*
77+
* @return the ID of the request, as returned by Stripe
78+
*/
4379
public String requestId() {
44-
return (headers != null) ? headers.firstValue("Request-Id").orElse(null) : null;
45-
}
46-
47-
public int numRetries() {
48-
return this.numRetries;
49-
}
50-
51-
void setNumRetries(int numRetries) {
52-
this.numRetries = numRetries;
80+
return this.headers.firstValue("Request-Id").orElse(null);
5381
}
5482
}

Diff for: src/test/java/com/stripe/net/HttpClientTest.java

+17-10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.stripe.exception.ApiConnectionException;
1313
import com.stripe.exception.StripeException;
1414
import java.net.ConnectException;
15+
import java.util.Collections;
1516
import org.junit.jupiter.api.BeforeEach;
1617
import org.junit.jupiter.api.Test;
1718
import org.mockito.Mockito;
@@ -21,6 +22,8 @@ public class HttpClientTest extends BaseStripeTest {
2122

2223
private StripeRequest request;
2324

25+
private HttpHeaders emptyHeaders = HttpHeaders.of(Collections.emptyMap());
26+
2427
@BeforeEach
2528
public void setUpFixtures() throws StripeException {
2629
this.client =
@@ -37,7 +40,7 @@ public void setUpFixtures() throws StripeException {
3740
public void testRequestWithRetriesConnectException() throws StripeException {
3841
Mockito.when(this.client.request(this.request))
3942
.thenThrow(new ApiConnectionException("foo", new ConnectException("timeout or something")))
40-
.thenReturn(new StripeResponse(200, "{}"));
43+
.thenReturn(new StripeResponse(200, emptyHeaders, "{}"));
4144

4245
StripeResponse response = this.client.requestWithRetries(this.request);
4346

@@ -71,8 +74,10 @@ public void testRequestWithRetriesStripeShouldRetryTrue() throws StripeException
7174
Mockito.when(this.client.request(this.request))
7275
.thenReturn(
7376
new StripeResponse(
74-
400, "{}", ImmutableMap.of("Stripe-Should-Retry", ImmutableList.of("true"))))
75-
.thenReturn(new StripeResponse(200, "{}"));
77+
400,
78+
HttpHeaders.of(ImmutableMap.of("Stripe-Should-Retry", ImmutableList.of("true"))),
79+
"{}"))
80+
.thenReturn(new StripeResponse(200, emptyHeaders, "{}"));
7681

7782
StripeResponse response = this.client.requestWithRetries(this.request);
7883

@@ -86,7 +91,9 @@ public void testRequestWithRetriesStripeShouldRetryFalse() throws StripeExceptio
8691
Mockito.when(this.client.request(this.request))
8792
.thenReturn(
8893
new StripeResponse(
89-
400, "{}", ImmutableMap.of("Stripe-Should-Retry", ImmutableList.of("false"))));
94+
400,
95+
HttpHeaders.of(ImmutableMap.of("Stripe-Should-Retry", ImmutableList.of("false"))),
96+
"{}"));
9097

9198
StripeResponse response = this.client.requestWithRetries(this.request);
9299

@@ -98,8 +105,8 @@ public void testRequestWithRetriesStripeShouldRetryFalse() throws StripeExceptio
98105
@Test
99106
public void testRequestWithRetriesConflict() throws StripeException {
100107
Mockito.when(this.client.request(this.request))
101-
.thenReturn(new StripeResponse(409, "{}"))
102-
.thenReturn(new StripeResponse(200, "{}"));
108+
.thenReturn(new StripeResponse(409, emptyHeaders, "{}"))
109+
.thenReturn(new StripeResponse(200, emptyHeaders, "{}"));
103110

104111
StripeResponse response = this.client.requestWithRetries(this.request);
105112

@@ -111,8 +118,8 @@ public void testRequestWithRetriesConflict() throws StripeException {
111118
@Test
112119
public void testRequestWithRetriesConflictServiceUnavailable() throws StripeException {
113120
Mockito.when(this.client.request(this.request))
114-
.thenReturn(new StripeResponse(503, "{}"))
115-
.thenReturn(new StripeResponse(200, "{}"));
121+
.thenReturn(new StripeResponse(503, emptyHeaders, "{}"))
122+
.thenReturn(new StripeResponse(200, emptyHeaders, "{}"));
116123

117124
StripeResponse response = this.client.requestWithRetries(this.request);
118125

@@ -124,8 +131,8 @@ public void testRequestWithRetriesConflictServiceUnavailable() throws StripeExce
124131
@Test
125132
public void testRequestWithRetriesConflictInternalServerError() throws StripeException {
126133
Mockito.when(this.client.request(this.request))
127-
.thenReturn(new StripeResponse(500, "{}"))
128-
.thenReturn(new StripeResponse(200, "{}"));
134+
.thenReturn(new StripeResponse(500, emptyHeaders, "{}"))
135+
.thenReturn(new StripeResponse(200, emptyHeaders, "{}"));
129136

130137
StripeResponse response = this.client.requestWithRetries(this.request);
131138

Diff for: src/test/java/com/stripe/net/StripeResponseTest.java

+43-30
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,85 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
57

8+
import com.google.common.collect.ImmutableList;
9+
import com.google.common.collect.ImmutableMap;
610
import com.stripe.BaseStripeTest;
7-
import java.util.ArrayList;
8-
import java.util.HashMap;
11+
import java.time.Instant;
12+
import java.util.Collections;
913
import java.util.List;
1014
import java.util.Map;
1115
import org.junit.jupiter.api.Test;
1216

1317
public class StripeResponseTest extends BaseStripeTest {
14-
String chargeBody;
18+
private HttpHeaders emptyHeaders = HttpHeaders.of(Collections.emptyMap());
1519

16-
private Map<String, List<String>> generateHeaderMap() {
17-
final List<String> idempotencyHeader = new ArrayList<>();
18-
idempotencyHeader.add("12345");
19-
20-
final List<String> requestIdHeader = new ArrayList<>();
21-
requestIdHeader.add("req_12345");
22-
23-
final Map<String, List<String>> headerMap = new HashMap<>();
24-
headerMap.put("Idempotency-Key", idempotencyHeader);
25-
headerMap.put("Request-Id", requestIdHeader);
20+
@Test
21+
public void testCtorNullHeaders() {
22+
assertThrows(
23+
NullPointerException.class,
24+
() -> {
25+
new StripeResponse(200, null, "");
26+
});
27+
}
2628

27-
return headerMap;
29+
@Test
30+
public void testCtorNullBody() {
31+
assertThrows(
32+
NullPointerException.class,
33+
() -> {
34+
new StripeResponse(200, emptyHeaders, null);
35+
});
2836
}
2937

3038
@Test
3139
public void testCode() {
32-
StripeResponse stripeResponse = new StripeResponse(200, chargeBody);
40+
StripeResponse stripeResponse = new StripeResponse(200, emptyHeaders, "");
3341
assertEquals(200, stripeResponse.code());
34-
stripeResponse = new StripeResponse(201, chargeBody);
42+
stripeResponse = new StripeResponse(201, emptyHeaders, "");
3543
assertEquals(201, stripeResponse.code());
3644
}
3745

3846
@Test
3947
public void testBody() {
40-
final StripeResponse stripeResponse = new StripeResponse(200, chargeBody);
48+
final StripeResponse stripeResponse = new StripeResponse(200, emptyHeaders, "Response body");
4149
assertEquals(200, stripeResponse.code());
42-
assertEquals(chargeBody, stripeResponse.body());
50+
assertEquals("Response body", stripeResponse.body());
4351
}
4452

4553
@Test
4654
public void testHeaders() {
47-
final Map<String, List<String>> headerMap = generateHeaderMap();
48-
final StripeResponse stripeResponse = new StripeResponse(200, chargeBody, headerMap);
55+
Map<String, List<String>> headerMap =
56+
ImmutableMap.of("Some-Header", ImmutableList.of("First value", "Second value"));
57+
final StripeResponse stripeResponse = new StripeResponse(200, HttpHeaders.of(headerMap), "");
4958
assertNotNull(stripeResponse.headers());
59+
assertTrue(stripeResponse.headers().firstValue("Some-Header").isPresent());
60+
assertEquals("First value", stripeResponse.headers().firstValue("Some-Header").get());
5061
}
5162

5263
@Test
53-
public void testNoHeaders() {
54-
final StripeResponse stripeResponse = new StripeResponse(200, chargeBody);
55-
assertEquals(stripeResponse.headers(), null);
56-
assertEquals(stripeResponse.idempotencyKey(), null);
57-
assertEquals(stripeResponse.requestId(), null);
64+
public void testDate() {
65+
Map<String, List<String>> headerMap =
66+
ImmutableMap.of("Date", ImmutableList.of("Fri, 13 Feb 2009 23:31:30 GMT"));
67+
final StripeResponse stripeResponse = new StripeResponse(200, HttpHeaders.of(headerMap), "");
68+
assertEquals(Instant.ofEpochSecond(1234567890), stripeResponse.date());
5869
}
5970

6071
@Test
61-
public void testGetIdempotencyKey() {
62-
final Map<String, List<String>> headerMap = generateHeaderMap();
63-
final StripeResponse stripeResponse = new StripeResponse(200, chargeBody, headerMap);
72+
public void testIdempotencyKey() {
73+
Map<String, List<String>> headerMap =
74+
ImmutableMap.of("Idempotency-Key", ImmutableList.of("12345"));
75+
final StripeResponse stripeResponse = new StripeResponse(200, HttpHeaders.of(headerMap), "");
6476
assertEquals("12345", stripeResponse.idempotencyKey());
6577
}
6678

6779
@Test
6880
public void testRequestId() {
69-
final Map<String, List<String>> headerMap = generateHeaderMap();
70-
final StripeResponse stripeResponse = new StripeResponse(200, chargeBody, headerMap);
81+
Map<String, List<String>> headerMap =
82+
ImmutableMap.of("Request-Id", ImmutableList.of("req_12345"));
83+
final StripeResponse stripeResponse = new StripeResponse(200, HttpHeaders.of(headerMap), "");
7184
assertEquals("req_12345", stripeResponse.requestId());
7285
}
7386
}

0 commit comments

Comments
 (0)