Skip to content

Commit 4395b10

Browse files
authored
Merge pull request #101 from maxmind/greg/email-domain-first-seen
Support /email/domain/first_seen and use Java 8 time types throughout
2 parents 0a43531 + 2a86bcf commit 4395b10

22 files changed

Lines changed: 236 additions & 32 deletions

CHANGELOG.md

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

4+
1.12.0
5+
-------------------
6+
7+
* Added support for the new email output `/email/domain/first_seen` This can
8+
be accessed via `response.getEmail().getDomain().getFirstSeen()`.
9+
* Added `Device.getLastSeenDateTime()`, `Device.getLocalDateTime()`,
10+
`Email.getFirstSeenDate()`, and `GeoIp2Location.getLocalDateTime()` methods
11+
that return `java.time` objects rather than strings.
12+
* The request event time is now stored internally as a `ZonedDateTime`. An
13+
`Event.Builder.time(ZonedDateTime)` method was also added to the event
14+
builder.
15+
416
1.11.0 (2020-02-21)
517
-------------------
618

pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,18 @@
4747
<dependency>
4848
<groupId>com.fasterxml.jackson.core</groupId>
4949
<artifactId>jackson-databind</artifactId>
50-
<version>2.10.2</version>
50+
<version>2.10.3</version>
5151
</dependency>
5252
<dependency>
5353
<groupId>com.fasterxml.jackson.core</groupId>
5454
<artifactId>jackson-annotations</artifactId>
5555
<version>2.10.3</version>
5656
</dependency>
57+
<dependency>
58+
<groupId>com.fasterxml.jackson.datatype</groupId>
59+
<artifactId>jackson-datatype-jsr310</artifactId>
60+
<version>2.10.3</version>
61+
</dependency>
5762
<dependency>
5863
<groupId>com.maxmind.geoip2</groupId>
5964
<artifactId>geoip2</artifactId>

src/main/java/com/maxmind/minfraud/AbstractModel.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.fasterxml.jackson.databind.ObjectMapper;
66
import com.fasterxml.jackson.databind.SerializationFeature;
77
import com.fasterxml.jackson.databind.util.StdDateFormat;
8+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
89

910
import java.io.IOException;
1011

@@ -16,12 +17,13 @@ public abstract class AbstractModel {
1617
*/
1718
public final String toJson() throws IOException {
1819
ObjectMapper mapper = new ObjectMapper();
20+
mapper.registerModule(new JavaTimeModule());
1921
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
2022
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
2123
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
2224
mapper.disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS);
2325
mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
24-
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
26+
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
2527

2628
return mapper.writeValueAsString(this);
2729
}

src/main/java/com/maxmind/minfraud/WebServiceClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.fasterxml.jackson.databind.MapperFeature;
88
import com.fasterxml.jackson.databind.ObjectMapper;
99
import com.fasterxml.jackson.databind.util.StdDateFormat;
10+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
1011
import com.maxmind.minfraud.exception.*;
1112
import com.maxmind.minfraud.request.Transaction;
1213
import com.maxmind.minfraud.response.FactorsResponse;
@@ -62,6 +63,7 @@ private WebServiceClient(WebServiceClient.Builder builder) {
6263
mapper = new ObjectMapper();
6364
mapper.disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS);
6465
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
66+
mapper.registerModule(new JavaTimeModule());
6567
mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
6668

6769
RequestConfig.Builder configBuilder = RequestConfig.custom()

src/main/java/com/maxmind/minfraud/request/Event.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package com.maxmind.minfraud.request;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.fasterxml.jackson.annotation.JsonProperty;
45
import com.maxmind.minfraud.AbstractModel;
56

7+
import java.time.ZoneId;
8+
import java.time.ZonedDateTime;
69
import java.util.Date;
710

811
public final class Event extends AbstractModel {
912

1013
private final String transactionId;
1114
private final String shopId;
12-
private final Date time;
15+
private final ZonedDateTime time;
1316
private final Type type;
1417

1518
private Event(Event.Builder builder) {
@@ -26,7 +29,7 @@ private Event(Event.Builder builder) {
2629
public static final class Builder {
2730
String transactionId;
2831
String shopId;
29-
Date time;
32+
ZonedDateTime time;
3033
Type type;
3134

3235
/**
@@ -58,7 +61,16 @@ public Event.Builder shopId(String id) {
5861
* @return The builder object.
5962
*/
6063
public Event.Builder time(Date date) {
61-
time = new Date(date.getTime());
64+
time = date.toInstant().atZone(ZoneId.systemDefault());
65+
return this;
66+
}
67+
68+
/**
69+
* @param date The date and time the event occurred.
70+
* @return The builder object.
71+
*/
72+
public Event.Builder time(ZonedDateTime date) {
73+
time = date;
6274
return this;
6375
}
6476

@@ -99,8 +111,16 @@ public String getShopId() {
99111
/**
100112
* @return The date and time of the event.
101113
*/
102-
@JsonProperty("time")
114+
@JsonIgnore
103115
public Date getTime() {
116+
return Date.from(time.toInstant());
117+
}
118+
119+
/**
120+
* @return The date and time of the event.
121+
*/
122+
@JsonProperty("time")
123+
public ZonedDateTime getDateTime() {
104124
return time;
105125
}
106126

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.maxmind.minfraud.response;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.fasterxml.jackson.annotation.JsonProperty;
45
import com.maxmind.minfraud.AbstractModel;
56

7+
import java.time.ZonedDateTime;
68
import java.util.UUID;
79

810
/**
@@ -73,6 +75,18 @@ public String getLastSeen() {
7375
return lastSeen;
7476
}
7577

78+
/**
79+
* @return The date and time of the last sighting of the device as a
80+
* {@code ZonedDateTime}.
81+
*/
82+
@JsonIgnore
83+
public ZonedDateTime getLastSeenDateTime() {
84+
if (lastSeen == null) {
85+
return null;
86+
}
87+
return ZonedDateTime.parse(lastSeen);
88+
}
89+
7690
/**
7791
* @return The date and time of the transaction at the UTC offset
7892
* associated with the device. This is an RFC 3339 date-time.
@@ -81,4 +95,16 @@ public String getLastSeen() {
8195
public String getLocalTime() {
8296
return localTime;
8397
}
98+
99+
/**
100+
* @return The date and time of the transaction at the UTC offset
101+
* associated with the device as a {@code ZonedDateTime}.
102+
*/
103+
@JsonIgnore
104+
public ZonedDateTime getLocalDateTime() {
105+
if (localTime == null) {
106+
return null;
107+
}
108+
return ZonedDateTime.parse(localTime);
109+
}
84110
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.maxmind.minfraud.response;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.fasterxml.jackson.annotation.JsonProperty;
45
import com.maxmind.minfraud.AbstractModel;
56

7+
import java.time.LocalDate;
8+
69
/**
710
* This class contains minFraud response data related to the email address.
811
*/
@@ -11,13 +14,16 @@ public final class Email extends AbstractModel {
1114
private final Boolean isFree;
1215
private final Boolean isHighRisk;
1316
private final String firstSeen;
17+
private final EmailDomain domain;
1418

1519
public Email(
20+
@JsonProperty("domain") EmailDomain domain,
1621
@JsonProperty("is_disposable") Boolean isDisposable,
1722
@JsonProperty("is_free") Boolean isFree,
1823
@JsonProperty("is_high_risk") Boolean isHighRisk,
1924
@JsonProperty("first_seen") String firstSeen
2025
) {
26+
this.domain = domain;
2127
this.isDisposable = isDisposable;
2228
this.isFree = isFree;
2329
this.isHighRisk = isHighRisk;
@@ -26,6 +32,15 @@ public Email(
2632

2733
// The following constructors are for backward compatibility and
2834
// can be removed as part of a major release
35+
public Email(
36+
Boolean isDisposable,
37+
Boolean isFree,
38+
Boolean isHighRisk,
39+
String firstSeen
40+
) {
41+
this(null, isDisposable, isFree, isHighRisk, firstSeen);
42+
}
43+
2944
public Email(
3045
Boolean isFree,
3146
Boolean isHighRisk,
@@ -45,6 +60,12 @@ public Email() {
4560
this(null, null, null);
4661
}
4762

63+
/**
64+
* @return The {@code EmailDomain} model object.
65+
*/
66+
public EmailDomain getDomain() {
67+
return domain;
68+
}
4869

4970
/**
5071
* @return Whether the email address is from a disposable email provider.
@@ -84,4 +105,16 @@ public Boolean isHighRisk() {
84105
public String getFirstSeen() {
85106
return firstSeen;
86107
}
108+
109+
/**
110+
* @return A date to identify the date an email address was first seen by
111+
* MaxMind.
112+
*/
113+
@JsonIgnore
114+
public LocalDate getFirstSeenDate() {
115+
if (firstSeen == null) {
116+
return null;
117+
}
118+
return LocalDate.parse(firstSeen);
119+
}
87120
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.maxmind.minfraud.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.maxmind.minfraud.AbstractModel;
5+
6+
import java.time.LocalDate;
7+
8+
/**
9+
* This class contains minFraud response data related to the email domain.
10+
*/
11+
public final class EmailDomain extends AbstractModel {
12+
private final LocalDate firstSeen;
13+
14+
public EmailDomain(
15+
@JsonProperty("first_seen") LocalDate firstSeen
16+
) {
17+
this.firstSeen = firstSeen;
18+
}
19+
20+
/**
21+
* @return A date to identify the date an email domain was first
22+
* seen by MaxMind.
23+
*/
24+
@JsonProperty("first_seen")
25+
public LocalDate getFirstSeen() {
26+
return firstSeen;
27+
}
28+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.maxmind.minfraud.response;
22

3+
import com.fasterxml.jackson.annotation.JsonIgnore;
34
import com.fasterxml.jackson.annotation.JsonProperty;
45
import com.maxmind.geoip2.record.Location;
56

7+
import java.time.ZonedDateTime;
8+
69
/**
710
* This class contains minFraud response data related to the GeoIP2 Insights
811
* location.
@@ -38,4 +41,16 @@ public GeoIp2Location() {
3841
public String getLocalTime() {
3942
return this.localTime;
4043
}
44+
45+
/**
46+
* @return The date and time of the transaction in the time zone associated
47+
* with the IP address as a {@code ZonedDateTime}.
48+
*/
49+
@JsonIgnore
50+
public ZonedDateTime getLocalDateTime() {
51+
if (localTime == null) {
52+
return null;
53+
}
54+
return ZonedDateTime.parse(localTime);
55+
}
4156
}

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,11 @@ public void test200WithInvalidJson() throws Exception {
151151

152152
@Test
153153
public void testInsufficientCredit() throws Exception {
154-
Exception ex = assertThrows(InsufficientFundsException.class, () -> {
155-
createInsightsError(
156-
402,
157-
"application/json",
158-
"{\"code\":\"INSUFFICIENT_FUNDS\",\"error\":\"out of credit\"}"
159-
);
160-
});
154+
Exception ex = assertThrows(InsufficientFundsException.class, () -> createInsightsError(
155+
402,
156+
"application/json",
157+
"{\"code\":\"INSUFFICIENT_FUNDS\",\"error\":\"out of credit\"}"
158+
));
161159
assertEquals("out of credit", ex.getMessage());
162160

163161
}
@@ -180,13 +178,11 @@ public void testInvalidAuth(String code) throws Exception {
180178

181179
@Test
182180
public void testPermissionRequired() throws Exception {
183-
Exception ex = assertThrows(PermissionRequiredException.class, () -> {
184-
createInsightsError(
185-
403,
186-
"application/json",
187-
"{\"code\":\"PERMISSION_REQUIRED\",\"error\":\"Permission required\"}"
188-
);
189-
});
181+
Exception ex = assertThrows(PermissionRequiredException.class, () -> createInsightsError(
182+
403,
183+
"application/json",
184+
"{\"code\":\"PERMISSION_REQUIRED\",\"error\":\"Permission required\"}"
185+
));
190186
assertEquals("Permission required", ex.getMessage());
191187
}
192188

0 commit comments

Comments
 (0)