Skip to content

Commit e48aac9

Browse files
authored
Merge pull request #91 from maxmind/greg/is-disposable
Add support for /email/is_disposable
2 parents 0bcc146 + 02db5cd commit e48aac9

6 files changed

Lines changed: 126 additions & 81 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
1.11.0
5+
-------------------
6+
7+
* Added support for the new email output `/email/is_disposable`. This can
8+
be accessed via the `isDisposable()` method on the `Email` response
9+
object.
10+
411
1.10.0 (2019-12-19)
512
-------------------
613

src/main/java/com/maxmind/minfraud/response/Email.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,68 @@
77
* This class contains minFraud response data related to the email address.
88
*/
99
public final class Email extends AbstractModel {
10+
private final Boolean isDisposable;
1011
private final Boolean isFree;
1112
private final Boolean isHighRisk;
1213
private final String firstSeen;
1314

1415
public Email(
16+
@JsonProperty("is_disposable") Boolean isDisposable,
1517
@JsonProperty("is_free") Boolean isFree,
1618
@JsonProperty("is_high_risk") Boolean isHighRisk,
1719
@JsonProperty("first_seen") String firstSeen
1820
) {
21+
this.isDisposable = isDisposable;
1922
this.isFree = isFree;
2023
this.isHighRisk = isHighRisk;
2124
this.firstSeen = firstSeen;
2225
}
2326

27+
// The following constructors are for backward compatibility and
28+
// can be removed as part of a major release
29+
public Email(
30+
Boolean isFree,
31+
Boolean isHighRisk,
32+
String firstSeen
33+
) {
34+
this(null, isFree, isHighRisk, firstSeen);
35+
}
36+
2437
public Email(
2538
Boolean isFree,
2639
Boolean isHighRisk
2740
) {
28-
this(isFree, isHighRisk, null);
41+
this(null, isFree, isHighRisk, null);
2942
}
3043

3144
public Email() {
3245
this(null, null, null);
3346
}
3447

48+
49+
/**
50+
* @return Whether the email address is from a disposable email provider.
51+
* If no email address was passed, this will be {@code null}.
52+
*/
53+
@JsonProperty("is_disposable")
54+
public Boolean isDisposable() {
55+
return isDisposable;
56+
}
57+
3558
/**
36-
* @return True if the email address is for a free email service provider.
59+
* /**
60+
*
61+
* @return Whether the email address is from a free email provider such as
62+
* Gmail. If no email address was passed, this will be {@code null}.
3763
*/
3864
@JsonProperty("is_free")
3965
public Boolean isFree() {
4066
return isFree;
4167
}
4268

4369
/**
44-
* @return True if the email address is associated with fraud.
70+
* @return Whether the email address is associated with fraud. If no email
71+
* address was passed, this will be {@code null}.
4572
*/
4673
@JsonProperty("is_high_risk")
4774
public Boolean isHighRisk() {

src/test/java/com/maxmind/minfraud/WebServiceClientTest.java

Lines changed: 84 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import junitparams.Parameters;
1313
import org.junit.Rule;
1414
import org.junit.Test;
15-
import org.junit.rules.ExpectedException;
1615
import org.junit.runner.RunWith;
1716
import org.skyscreamer.jsonassert.JSONAssert;
1817

@@ -22,16 +21,11 @@
2221
import static com.jcabi.matchers.RegexMatchers.matchesPattern;
2322
import static com.maxmind.minfraud.request.RequestTestHelper.*;
2423
import static org.hamcrest.core.StringStartsWith.startsWith;
25-
import static org.junit.Assert.assertEquals;
26-
import static org.junit.Assert.assertFalse;
27-
import static org.junit.Assert.assertTrue;
24+
import static org.junit.Assert.*;
25+
import static org.hamcrest.MatcherAssert.assertThat;
2826

2927
@RunWith(JUnitParamsRunner.class)
3028
public class WebServiceClientTest {
31-
32-
@Rule
33-
public final ExpectedException thrown = ExpectedException.none();
34-
3529
@Rule
3630
public final WireMockRule wireMockRule = new WireMockRule(0); // 0 picks random port
3731

@@ -82,7 +76,7 @@ public void testFullInsightsTransaction() throws Exception {
8276
response.getIpAddress().getRepresentedCountry().isInEuropeanUnion());
8377
assertEquals("2018-04-05T15:34:40-07:00", response.getDevice().getLocalTime());
8478

85-
assertEquals("81.2.69.160", response.getIpAddress().getTraits().getIpAddress() );
79+
assertEquals("81.2.69.160", response.getIpAddress().getTraits().getIpAddress());
8680
assertEquals("81.2.69.0/24", response.getIpAddress().getTraits().getNetwork().toString());
8781

8882
assertTrue(response.getCreditCard().isVirtual());
@@ -112,7 +106,7 @@ public void testFullFactorsTransaction() throws Exception {
112106
response.getIpAddress().getRepresentedCountry().isInEuropeanUnion());
113107

114108

115-
assertEquals("81.2.69.160", response.getIpAddress().getTraits().getIpAddress() );
109+
assertEquals("81.2.69.160", response.getIpAddress().getTraits().getIpAddress());
116110
assertEquals("81.2.69.0/24", response.getIpAddress().getTraits().getNetwork().toString());
117111
}
118112
}
@@ -138,10 +132,9 @@ public void testRequestEncoding() throws Exception {
138132
public void test200WithNoBody() throws Exception {
139133
try (WebServiceClient client = createSuccessClient("insights", "")) {
140134
Transaction request = fullTransaction();
135+
Exception ex = assertThrows(MinFraudException.class, () -> client.insights(request));
141136

142-
thrown.expect(MinFraudException.class);
143-
thrown.expectMessage(matchesPattern("Received a 200 response but could not decode it as JSON"));
144-
client.insights(request);
137+
assertThat(ex.getMessage(), matchesPattern("Received a 200 response but could not decode it as JSON"));
145138
}
146139
}
147140

@@ -150,21 +143,23 @@ public void test200WithInvalidJson() throws Exception {
150143
try (WebServiceClient client = createSuccessClient("insights", "{")) {
151144
Transaction request = fullTransaction();
152145

153-
thrown.expect(MinFraudException.class);
154-
thrown.expectMessage("Received a 200 response but could not decode it as JSON");
155-
client.insights(request);
146+
Exception ex = assertThrows(MinFraudException.class, () -> client.insights(request));
147+
148+
assertEquals("Received a 200 response but could not decode it as JSON", ex.getMessage());
156149
}
157150
}
158151

159152
@Test
160153
public void testInsufficientCredit() throws Exception {
161-
thrown.expect(InsufficientFundsException.class);
162-
thrown.expectMessage("out of credit");
163-
createInsightsError(
164-
402,
165-
"application/json",
166-
"{\"code\":\"INSUFFICIENT_FUNDS\",\"error\":\"out of credit\"}"
167-
);
154+
Exception ex = assertThrows(InsufficientFundsException.class, () -> {
155+
createInsightsError(
156+
402,
157+
"application/json",
158+
"{\"code\":\"INSUFFICIENT_FUNDS\",\"error\":\"out of credit\"}"
159+
);
160+
});
161+
assertEquals("out of credit", ex.getMessage());
162+
168163
}
169164

170165
@Test
@@ -173,101 +168,112 @@ public void testInsufficientCredit() throws Exception {
173168
"LICENSE_KEY_REQUIRED",
174169
"USER_ID_REQUIRED"})
175170
public void testInvalidAuth(String code) throws Exception {
176-
thrown.expect(AuthenticationException.class);
177-
thrown.expectMessage("Invalid auth");
178-
createInsightsError(
179-
401,
180-
"application/json",
181-
"{\"code\":\"" + code + "\",\"error\":\"Invalid auth\"}"
171+
Exception ex = assertThrows(AuthenticationException.class, () ->
172+
createInsightsError(
173+
401,
174+
"application/json",
175+
"{\"code\":\"" + code + "\",\"error\":\"Invalid auth\"}"
176+
)
182177
);
178+
assertEquals("Invalid auth", ex.getMessage());
183179
}
184180

185181
@Test
186182
public void testPermissionRequired() throws Exception {
187-
thrown.expect(PermissionRequiredException.class);
188-
thrown.expectMessage("Permission required");
189-
createInsightsError(
190-
403,
191-
"application/json",
192-
"{\"code\":\"PERMISSION_REQUIRED\",\"error\":\"Permission required\"}"
193-
);
183+
Exception ex = assertThrows(PermissionRequiredException.class, () -> {
184+
createInsightsError(
185+
403,
186+
"application/json",
187+
"{\"code\":\"PERMISSION_REQUIRED\",\"error\":\"Permission required\"}"
188+
);
189+
});
190+
assertEquals("Permission required", ex.getMessage());
194191
}
195192

196193
@Test
197194
public void testInvalidRequest() throws Exception {
198-
thrown.expect(InvalidRequestException.class);
199-
thrown.expectMessage("IP invalid");
200-
createInsightsError(
201-
400,
202-
"application/json",
203-
"{\"code\":\"IP_ADDRESS_INVALID\",\"error\":\"IP invalid\"}"
195+
Exception ex = assertThrows(InvalidRequestException.class, () ->
196+
createInsightsError(
197+
400,
198+
"application/json",
199+
"{\"code\":\"IP_ADDRESS_INVALID\",\"error\":\"IP invalid\"}"
200+
)
204201
);
202+
assertEquals("IP invalid", ex.getMessage());
205203
}
206204

207205
@Test
208206
public void test400WithInvalidJson() throws Exception {
209-
thrown.expect(HttpException.class);
210-
thrown.expectMessage(matchesPattern("Received a 400 error for .*/minfraud/v2.0/insights but it did not include the expected JSON body: \\{blah\\}"));
211-
createInsightsError(
212-
400,
213-
"application/json",
214-
"{blah}"
207+
Exception ex = assertThrows(HttpException.class, () ->
208+
createInsightsError(
209+
400,
210+
"application/json",
211+
"{blah}"
212+
)
215213
);
214+
assertThat(ex.getMessage(), matchesPattern("Received a 400 error for .*/minfraud/v2.0/insights but it did not include the expected JSON body: \\{blah\\}"));
216215
}
217216

218217
@Test
219218
public void test400WithNoBody() throws Exception {
220-
thrown.expect(HttpException.class);
221-
thrown.expectMessage(matchesPattern("Received a 400 error for .*/minfraud/v2.0/insights but it did not include the expected JSON body:.*"));
222-
createInsightsError(
223-
400,
224-
"application/json",
225-
""
219+
Exception ex = assertThrows(HttpException.class, () ->
220+
createInsightsError(
221+
400,
222+
"application/json",
223+
""
224+
)
226225
);
226+
assertThat(ex.getMessage(), matchesPattern("Received a 400 error for .*/minfraud/v2.0/insights but it did not include the expected JSON body:.*"));
227227
}
228228

229229
@Test
230230
public void test400WithUnexpectedContentType() throws Exception {
231-
thrown.expect(HttpException.class);
232-
thrown.expectMessage(matchesPattern("Received a 400 error for .*/minfraud/v2.0/insights but it did not include the expected JSON body: text"));
233-
createInsightsError(
234-
400,
235-
"text/plain",
236-
"text"
231+
Exception ex = assertThrows(HttpException.class, () ->
232+
createInsightsError(
233+
400,
234+
"text/plain",
235+
"text"
236+
)
237237
);
238+
assertThat(ex.getMessage(), matchesPattern("Received a 400 error for .*/minfraud/v2.0/insights but it did not include the expected JSON body: text"));
239+
238240
}
239241

240242
@Test
241243
public void test400WithUnexpectedJson() throws Exception {
242-
thrown.expect(HttpException.class);
243-
thrown.expectMessage("Error response contains JSON but it does not specify code or error keys: {\"not\":\"expected\"}");
244-
createInsightsError(
245-
400,
246-
"application/json",
247-
"{\"not\":\"expected\"}"
244+
Exception ex = assertThrows(HttpException.class, () ->
245+
createInsightsError(
246+
400,
247+
"application/json",
248+
"{\"not\":\"expected\"}"
249+
)
248250
);
251+
assertEquals("Error response contains JSON but it does not specify code or error keys: {\"not\":\"expected\"}", ex.getMessage());
249252
}
250253

251254
@Test
252255
public void test300() throws Exception {
253-
thrown.expect(HttpException.class);
254-
thrown.expectMessage(startsWith("Received an unexpected HTTP status (300)"));
255-
createInsightsError(
256-
300,
257-
"application/json",
258-
""
256+
Exception ex = assertThrows(HttpException.class, () ->
257+
createInsightsError(
258+
300,
259+
"application/json",
260+
""
261+
)
259262
);
263+
assertThat(ex.getMessage(), startsWith("Received an unexpected HTTP status (300)"));
264+
260265
}
261266

262267
@Test
263268
public void test500() throws Exception {
264-
thrown.expect(HttpException.class);
265-
thrown.expectMessage(startsWith("Received a server error (500)"));
266-
createInsightsError(
267-
500,
268-
"application/json",
269-
""
269+
Exception ex = assertThrows(HttpException.class, () ->
270+
createInsightsError(
271+
500,
272+
"application/json",
273+
""
274+
)
270275
);
276+
assertThat(ex.getMessage(), startsWith("Received a server error (500)"));
271277
}
272278

273279
private WebServiceClient createSuccessClient(String service, String responseContent) {

src/test/java/com/maxmind/minfraud/response/EmailTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ public void testEmail() throws Exception {
1414
JSON.std
1515
.composeString()
1616
.startObject()
17+
.put("is_disposable", false)
1718
.put("is_free", false)
1819
.put("is_high_risk", true)
1920
.put("first_seen", "2017-01-02")
2021
.end()
2122
.finish()
2223
);
2324

25+
assertFalse(email.isDisposable());
2426
assertFalse(email.isFree());
2527
assertTrue(email.isHighRisk());
2628
assertEquals(email.getFirstSeen(), "2017-01-02");
@@ -39,6 +41,7 @@ public void testEmailWithoutFirstSeen() throws Exception {
3941
.finish()
4042
);
4143

44+
assertNull(email.isDisposable());
4245
assertFalse(email.isFree());
4346
assertTrue(email.isHighRisk());
4447
assertNull(email.getFirstSeen());

src/test/resources/test-data/factors-response.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
},
131131
"email": {
132132
"first_seen": "2017-01-02",
133+
"is_disposable": true,
133134
"is_free": true,
134135
"is_high_risk": true
135136
},

src/test/resources/test-data/insights-response.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
},
147147
"email": {
148148
"first_seen": "2017-01-02",
149+
"is_disposable": true,
149150
"is_free": true,
150151
"is_high_risk": true
151152
},

0 commit comments

Comments
 (0)