Show only the last of each event type in RDAP domain response

We also ignore events that happened before the domain was created (for example, in a previous incarnation of the same domain name) and we set the last changed event to be the later of the last EPP change and any other event that happened before "now".

From RDAP response profile
2.3.2 The domain object in the RDAP response MAY contain the following events:
2.3.2.3 An event of *eventAction* type *transfer*, with the last date and time that the
domain was transferred. The event of *eventAction* type *transfer* MUST be omitted if
the domain name has not been transferred since it was created.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=242461310
This commit is contained in:
guyben 2019-04-08 07:46:01 -07:00 committed by jianglai
parent 72b9ca6894
commit 15e54f2803
13 changed files with 126 additions and 54 deletions

View file

@ -54,6 +54,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -63,6 +64,7 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.joda.time.DateTime;
import org.joda.time.DateTimeComparator;
/**
* Helper class to create RDAP JSON objects for various registry entities and objects.
@ -1008,32 +1010,85 @@ public class RdapJsonFormatter {
* Creates an event list for a domain, host or contact resource.
*/
private static ImmutableList<Object> makeEvents(EppResource resource, DateTime now) {
ImmutableList.Builder<Object> eventsBuilder = new ImmutableList.Builder<>();
for (HistoryEntry historyEntry : ofy().load()
.type(HistoryEntry.class)
.ancestor(resource)
.order("modificationTime")) {
// Only create an event if this is a type we care about.
if (!historyEntryTypeToRdapEventActionMap.containsKey(historyEntry.getType())) {
HashMap<RdapEventAction, HistoryEntry> lastEntryOfType = Maps.newHashMap();
// Events (such as transfer, but also create) can appear multiple times. We only want the last
// time they appeared.
//
// We can have multiple create historyEntries if a domain was deleted, and then someone new
// bought it.
//
// From RDAP response profile
// 2.3.2 The domain object in the RDAP response MAY contain the following events:
// 2.3.2.3 An event of *eventAction* type *transfer*, with the last date and time that the
// domain was transferred. The event of *eventAction* type *transfer* MUST be omitted if the
// domain name has not been transferred since it was created.
for (HistoryEntry historyEntry :
ofy().load().type(HistoryEntry.class).ancestor(resource).order("modificationTime")) {
RdapEventAction rdapEventAction =
historyEntryTypeToRdapEventActionMap.get(historyEntry.getType());
// Only save the historyEntries if this is a type we care about.
if (rdapEventAction == null) {
continue;
}
RdapEventAction eventAction =
historyEntryTypeToRdapEventActionMap.get(historyEntry.getType());
eventsBuilder.add(makeEvent(
eventAction, historyEntry.getClientId(), historyEntry.getModificationTime()));
lastEntryOfType.put(rdapEventAction, historyEntry);
}
ImmutableList.Builder<Object> eventsBuilder = new ImmutableList.Builder<>();
// There are 2 possibly conflicting values for the creation time - either the
// resource.getCreationTime, or the REGISTRATION event created from a HistoryEntry
//
// We favor the HistoryEntry if it exists, since we show that value as REGISTRATION time in the
// reply, so the reply will be self-consistent.
//
// This is mostly an issue in the tests as in "reality" these two values should be the same.
//
DateTime creationTime =
Optional.ofNullable(lastEntryOfType.get(RdapEventAction.REGISTRATION))
.map(historyEntry -> historyEntry.getModificationTime())
.orElse(resource.getCreationTime());
// TODO(b/129849684) remove this and use the events List defined above once we have Event
// objects
ImmutableList.Builder<DateTime> changeTimesBuilder = new ImmutableList.Builder<>();
// The order of the elements is stable - it's the order in which the enum elements are defined
// in RdapEventAction
for (RdapEventAction rdapEventAction : RdapEventAction.values()) {
HistoryEntry historyEntry = lastEntryOfType.get(rdapEventAction);
// Check if there was any entry of this type
if (historyEntry == null) {
continue;
}
DateTime modificationTime = historyEntry.getModificationTime();
// We will ignore all events that happened before the "creation time", since these events are
// from a "previous incarnation of the domain" (for a domain that was owned by someone,
// deleted, and then bought by someone else)
if (modificationTime.isBefore(creationTime)) {
continue;
}
eventsBuilder.add(makeEvent(rdapEventAction, historyEntry.getClientId(), modificationTime));
changeTimesBuilder.add(modificationTime);
}
if (resource instanceof DomainBase) {
DateTime expirationTime = ((DomainBase) resource).getRegistrationExpirationTime();
if (expirationTime != null) {
eventsBuilder.add(makeEvent(RdapEventAction.EXPIRATION, null, expirationTime));
changeTimesBuilder.add(expirationTime);
}
}
if ((resource.getLastEppUpdateTime() != null)
&& resource.getLastEppUpdateTime().isAfter(resource.getCreationTime())) {
eventsBuilder.add(makeEvent(
RdapEventAction.LAST_CHANGED, null, resource.getLastEppUpdateTime()));
if (resource.getLastEppUpdateTime() != null) {
changeTimesBuilder.add(resource.getLastEppUpdateTime());
}
// The last change time might not be the lastEppUpdateTime, since some changes happen without
// any EPP update (for example, by the passage of time).
DateTime lastChangeTime =
changeTimesBuilder.build().stream()
.filter(changeTime -> changeTime.isBefore(now))
.max(DateTimeComparator.getInstance())
.orElse(null);
if (lastChangeTime != null && lastChangeTime.isAfter(creationTime)) {
eventsBuilder.add(makeEvent(RdapEventAction.LAST_CHANGED, null, lastChangeTime));
}
eventsBuilder.add(makeEvent(RdapEventAction.LAST_UPDATE_OF_RDAP_DATABASE, null, now));
// TODO(b/129849684): sort events by their time once we return a list of Events instead of JSON
// objects.
return eventsBuilder.build();
}

View file

@ -72,7 +72,7 @@ public class RdapJsonFormatterTest {
private Registrar registrar;
private DomainBase domainBaseFull;
private DomainBase domainBaseNoNameservers;
private DomainBase domainBaseNoNameserversNoTransfers;
private HostResource hostResourceIpv4;
private HostResource hostResourceIpv6;
private HostResource hostResourceBoth;
@ -195,7 +195,7 @@ public class RdapJsonFormatterTest {
hostResourceIpv4,
hostResourceIpv6,
registrar));
domainBaseNoNameservers = persistResource(
domainBaseNoNameserversNoTransfers = persistResource(
makeDomainBase(
"fish.みんな",
contactResourceRegistrant,
@ -224,14 +224,45 @@ public class RdapJsonFormatterTest {
HistoryEntry.Type.DOMAIN_CREATE,
Period.create(1, Period.Unit.YEARS),
"created",
clock.nowUtc()));
clock.nowUtc().minusMonths(4)));
persistResource(
makeHistoryEntry(
domainBaseNoNameservers,
domainBaseNoNameserversNoTransfers,
HistoryEntry.Type.DOMAIN_CREATE,
Period.create(1, Period.Unit.YEARS),
"created",
clock.nowUtc()));
// We create 3 "transfer approved" entries, to make sure we only save the last one
persistResource(
makeHistoryEntry(
domainBaseFull,
HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE,
null,
null,
clock.nowUtc().minusMonths(3)));
persistResource(
makeHistoryEntry(
domainBaseFull,
HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE,
null,
null,
clock.nowUtc().minusMonths(1)));
persistResource(
makeHistoryEntry(
domainBaseFull,
HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE,
null,
null,
clock.nowUtc().minusMonths(2)));
// We create a "transfer approved" entry for domainBaseNoNameserversNoTransfers that happened
// before the domain was created, to make sure we don't show it
persistResource(
makeHistoryEntry(
domainBaseNoNameserversNoTransfers,
HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE,
null,
null,
clock.nowUtc().minusMonths(3)));
}
public static ImmutableList<RegistrarContact> makeMoreRegistrarContacts(Registrar registrar) {
@ -553,9 +584,9 @@ public class RdapJsonFormatterTest {
}
@Test
public void testDomain_noNameservers() {
public void testDomain_noNameserversNoTransfers() {
assertThat(rdapJsonFormatter.makeRdapJsonForDomain(
domainBaseNoNameservers,
domainBaseNoNameserversNoTransfers,
false,
LINK_BASE,
WHOIS_SERVER,

View file

@ -23,6 +23,10 @@
"eventActor": "foo",
"eventDate": "1999-07-01T00:00:00.000Z"
},
{
"eventAction": "last changed",
"eventDate": "1999-07-01T00:00:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -24,10 +24,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -24,10 +24,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -32,7 +32,7 @@
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
"eventDate": "1999-07-01T00:00:00.000Z"
},
{
"eventAction": "last update of RDAP database",

View file

@ -26,10 +26,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -26,10 +26,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -25,10 +25,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -27,10 +27,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"

View file

@ -23,7 +23,12 @@
{
"eventAction": "registration",
"eventActor": "foo",
"eventDate": "2000-01-01T00:00:00.000Z"
"eventDate": "1999-09-01T00:00:00.000Z"
},
{
"eventAction": "transfer",
"eventActor": "foo",
"eventDate": "1999-12-01T00:00:00.000Z"
},
{
"eventAction": "expiration",
@ -31,7 +36,7 @@
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
"eventDate": "1999-12-01T00:00:00.000Z"
},
{
"eventAction": "last update of RDAP database",

View file

@ -23,7 +23,12 @@
{
"eventAction": "registration",
"eventActor": "foo",
"eventDate": "2000-01-01T00:00:00.000Z"
"eventDate": "1999-09-01T00:00:00.000Z"
},
{
"eventAction": "transfer",
"eventActor": "foo",
"eventDate": "1999-12-01T00:00:00.000Z"
},
{
"eventAction": "expiration",
@ -31,7 +36,7 @@
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
"eventDate": "1999-12-01T00:00:00.000Z"
},
{
"eventAction": "last update of RDAP database",

View file

@ -30,10 +30,6 @@
"eventAction": "expiration",
"eventDate": "2110-10-08T00:44:59.000Z"
},
{
"eventAction": "last changed",
"eventDate": "2009-05-29T20:13:00.000Z"
},
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"