mirror of
https://github.com/google/nomulus.git
synced 2025-05-29 08:50:09 +02:00
Convert poll-message-related classes to use SQL as well (#1050)
* Convert poll-message-related classes to use SQL as well Two relatively complex parts. The first is that we needed a small refactor on the AckPollMessagesCommand because we could theoretically be acking more poll messages than the Datastore transaction size boundary. This means that the normal flow of "gather the poll messages from the DB into one collection, then act on it" needs to be changed to a more functional flow. The second is that acking the poll message (deleting it in most cases) reduces the number of remaining poll messages in SQL but not in Datastore, since in Datastore the deletion does not take effect until after the transaction is over.
This commit is contained in:
parent
8e6a0d094b
commit
1fb08c4c17
12 changed files with 322 additions and 166 deletions
|
@ -16,15 +16,13 @@ package google.registry.flows.poll;
|
||||||
|
|
||||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||||
import static google.registry.flows.poll.PollFlowUtils.ackPollMessage;
|
import static google.registry.flows.poll.PollFlowUtils.ackPollMessage;
|
||||||
import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery;
|
import static google.registry.flows.poll.PollFlowUtils.getPollMessageCount;
|
||||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES;
|
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
||||||
import static google.registry.model.poll.PollMessageExternalKeyConverter.makePollMessageExternalId;
|
import static google.registry.model.poll.PollMessageExternalKeyConverter.makePollMessageExternalId;
|
||||||
import static google.registry.model.poll.PollMessageExternalKeyConverter.parsePollMessageExternalId;
|
import static google.registry.model.poll.PollMessageExternalKeyConverter.parsePollMessageExternalId;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||||
|
|
||||||
import com.googlecode.objectify.Key;
|
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||||
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||||
|
@ -39,6 +37,8 @@ import google.registry.model.poll.MessageQueueInfo;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.poll.PollMessageExternalKeyConverter;
|
import google.registry.model.poll.PollMessageExternalKeyConverter;
|
||||||
import google.registry.model.poll.PollMessageExternalKeyConverter.PollMessageExternalKeyParseException;
|
import google.registry.model.poll.PollMessageExternalKeyConverter.PollMessageExternalKeyParseException;
|
||||||
|
import google.registry.persistence.VKey;
|
||||||
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ public class PollAckFlow implements TransactionalFlow {
|
||||||
throw new MissingMessageIdException();
|
throw new MissingMessageIdException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Key<PollMessage> pollMessageKey;
|
VKey<PollMessage> pollMessageKey;
|
||||||
// Try parsing the messageId, and throw an exception if it's invalid.
|
// Try parsing the messageId, and throw an exception if it's invalid.
|
||||||
try {
|
try {
|
||||||
pollMessageKey = parsePollMessageExternalId(messageId);
|
pollMessageKey = parsePollMessageExternalId(messageId);
|
||||||
|
@ -84,12 +84,13 @@ public class PollAckFlow implements TransactionalFlow {
|
||||||
// Load the message to be acked. If a message is queued to be delivered in the future, we treat
|
// Load the message to be acked. If a message is queued to be delivered in the future, we treat
|
||||||
// it as if it doesn't exist yet. Same for if the message ID year isn't the same as the actual
|
// it as if it doesn't exist yet. Same for if the message ID year isn't the same as the actual
|
||||||
// poll message's event time (that means they're passing in an old already-acked ID).
|
// poll message's event time (that means they're passing in an old already-acked ID).
|
||||||
PollMessage pollMessage = ofy().load().key(pollMessageKey).now();
|
Optional<PollMessage> maybePollMessage = tm().loadByKeyIfPresent(pollMessageKey);
|
||||||
if (pollMessage == null
|
if (!maybePollMessage.isPresent()
|
||||||
|| !isBeforeOrAt(pollMessage.getEventTime(), now)
|
|| !isBeforeOrAt(maybePollMessage.get().getEventTime(), now)
|
||||||
|| !makePollMessageExternalId(pollMessage).equals(messageId)) {
|
|| !makePollMessageExternalId(maybePollMessage.get()).equals(messageId)) {
|
||||||
throw new MessageDoesNotExistException(messageId);
|
throw new MessageDoesNotExistException(messageId);
|
||||||
}
|
}
|
||||||
|
PollMessage pollMessage = maybePollMessage.get();
|
||||||
|
|
||||||
// Make sure this client is authorized to ack this message. It could be that the message is
|
// Make sure this client is authorized to ack this message. It could be that the message is
|
||||||
// supposed to go to a different registrar.
|
// supposed to go to a different registrar.
|
||||||
|
@ -106,8 +107,11 @@ public class PollAckFlow implements TransactionalFlow {
|
||||||
// acked, then we return a special status code indicating that. Note that the query will
|
// acked, then we return a special status code indicating that. Note that the query will
|
||||||
// include the message being acked.
|
// include the message being acked.
|
||||||
|
|
||||||
int messageCount = tm().doTransactionless(() -> getPollMessagesQuery(clientId, now).count());
|
int messageCount = tm().doTransactionless(() -> getPollMessageCount(clientId, now));
|
||||||
if (!includeAckedMessageInCount) {
|
// Within the same transaction, Datastore will not reflect the updated count (potentially
|
||||||
|
// reduced by one thanks to the acked poll message). SQL will, however, so we shouldn't reduce
|
||||||
|
// the count in the SQL case.
|
||||||
|
if (!includeAckedMessageInCount && tm().isOfy()) {
|
||||||
messageCount--;
|
messageCount--;
|
||||||
}
|
}
|
||||||
if (messageCount <= 0) {
|
if (messageCount <= 0) {
|
||||||
|
|
|
@ -16,25 +16,56 @@ package google.registry.flows.poll;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||||
|
|
||||||
import com.googlecode.objectify.cmd.Query;
|
import com.googlecode.objectify.cmd.Query;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
|
import java.util.Optional;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/** Static utility functions for poll flows. */
|
/** Static utility functions for poll flows. */
|
||||||
public final class PollFlowUtils {
|
public final class PollFlowUtils {
|
||||||
|
|
||||||
private PollFlowUtils() {}
|
public static final String SQL_POLL_MESSAGE_QUERY =
|
||||||
|
"FROM PollMessage WHERE clientId = :registrarId AND eventTime <= :now ORDER BY eventTime ASC";
|
||||||
|
private static final String SQL_POLL_MESSAGE_COUNT_QUERY =
|
||||||
|
"SELECT COUNT(*) FROM PollMessage WHERE clientId = :registrarId AND eventTime <= :now";
|
||||||
|
|
||||||
/** Returns a query for poll messages for the logged in registrar which are not in the future. */
|
/** Returns the number of poll messages for the given registrar that are not in the future. */
|
||||||
public static Query<PollMessage> getPollMessagesQuery(String clientId, DateTime now) {
|
public static int getPollMessageCount(String registrarId, DateTime now) {
|
||||||
return ofy().load()
|
if (tm().isOfy()) {
|
||||||
.type(PollMessage.class)
|
return datastorePollMessageQuery(registrarId, now).count();
|
||||||
.filter("clientId", clientId)
|
} else {
|
||||||
.filter("eventTime <=", now.toDate())
|
return jpaTm()
|
||||||
.order("eventTime");
|
.transact(
|
||||||
|
() ->
|
||||||
|
jpaTm()
|
||||||
|
.query(SQL_POLL_MESSAGE_COUNT_QUERY, Long.class)
|
||||||
|
.setParameter("registrarId", registrarId)
|
||||||
|
.setParameter("now", now)
|
||||||
|
.getSingleResult()
|
||||||
|
.intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the first (by event time) poll message not in the future for this registrar. */
|
||||||
|
public static Optional<PollMessage> getFirstPollMessage(String registrarId, DateTime now) {
|
||||||
|
if (tm().isOfy()) {
|
||||||
|
return Optional.ofNullable(datastorePollMessageQuery(registrarId, now).first().now());
|
||||||
|
} else {
|
||||||
|
return jpaTm()
|
||||||
|
.transact(
|
||||||
|
() ->
|
||||||
|
jpaTm()
|
||||||
|
.query(SQL_POLL_MESSAGE_QUERY, PollMessage.class)
|
||||||
|
.setParameter("registrarId", registrarId)
|
||||||
|
.setParameter("now", now)
|
||||||
|
.setMaxResults(1)
|
||||||
|
.getResultStream()
|
||||||
|
.findFirst());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,4 +105,16 @@ public final class PollFlowUtils {
|
||||||
}
|
}
|
||||||
return includeAckedMessageInCount;
|
return includeAckedMessageInCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A Datastore query for poll messages from the given registrar that are not in the future. */
|
||||||
|
public static Query<PollMessage> datastorePollMessageQuery(String registrarId, DateTime now) {
|
||||||
|
return ofy()
|
||||||
|
.load()
|
||||||
|
.type(PollMessage.class)
|
||||||
|
.filter("clientId", registrarId)
|
||||||
|
.filter("eventTime <=", now.toDate())
|
||||||
|
.order("eventTime");
|
||||||
|
}
|
||||||
|
|
||||||
|
private PollFlowUtils() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
package google.registry.flows.poll;
|
package google.registry.flows.poll;
|
||||||
|
|
||||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||||
import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery;
|
import static google.registry.flows.poll.PollFlowUtils.getFirstPollMessage;
|
||||||
|
import static google.registry.flows.poll.PollFlowUtils.getPollMessageCount;
|
||||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACK_MESSAGE;
|
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACK_MESSAGE;
|
||||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES;
|
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES;
|
||||||
import static google.registry.model.poll.PollMessageExternalKeyConverter.makePollMessageExternalId;
|
import static google.registry.model.poll.PollMessageExternalKeyConverter.makePollMessageExternalId;
|
||||||
|
@ -31,6 +32,7 @@ import google.registry.model.poll.MessageQueueInfo;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.poll.PollMessageExternalKeyConverter;
|
import google.registry.model.poll.PollMessageExternalKeyConverter;
|
||||||
import google.registry.util.Clock;
|
import google.registry.util.Clock;
|
||||||
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
@ -63,18 +65,20 @@ public class PollRequestFlow implements Flow {
|
||||||
}
|
}
|
||||||
// Return the oldest message from the queue.
|
// Return the oldest message from the queue.
|
||||||
DateTime now = clock.nowUtc();
|
DateTime now = clock.nowUtc();
|
||||||
PollMessage pollMessage = getPollMessagesQuery(clientId, now).first().now();
|
Optional<PollMessage> maybePollMessage = getFirstPollMessage(clientId, now);
|
||||||
if (pollMessage == null) {
|
if (!maybePollMessage.isPresent()) {
|
||||||
return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build();
|
return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build();
|
||||||
}
|
}
|
||||||
|
PollMessage pollMessage = maybePollMessage.get();
|
||||||
return responseBuilder
|
return responseBuilder
|
||||||
.setResultFromCode(SUCCESS_WITH_ACK_MESSAGE)
|
.setResultFromCode(SUCCESS_WITH_ACK_MESSAGE)
|
||||||
.setMessageQueueInfo(new MessageQueueInfo.Builder()
|
.setMessageQueueInfo(
|
||||||
.setQueueDate(pollMessage.getEventTime())
|
new MessageQueueInfo.Builder()
|
||||||
.setMsg(pollMessage.getMsg())
|
.setQueueDate(pollMessage.getEventTime())
|
||||||
.setQueueLength(getPollMessagesQuery(clientId, now).count())
|
.setMsg(pollMessage.getMsg())
|
||||||
.setMessageId(makePollMessageExternalId(pollMessage))
|
.setQueueLength(getPollMessageCount(clientId, now))
|
||||||
.build())
|
.setMessageId(makePollMessageExternalId(pollMessage))
|
||||||
|
.build())
|
||||||
.setMultipleResData(pollMessage.getResponseData())
|
.setMultipleResData(pollMessage.getResponseData())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||||
@Column(name = "poll_message_id")
|
@Column(name = "poll_message_id")
|
||||||
Long id;
|
Long id;
|
||||||
|
|
||||||
@Parent @DoNotHydrate @Transient Key<HistoryEntry> parent;
|
@Parent @DoNotHydrate @Transient Key<? extends HistoryEntry> parent;
|
||||||
|
|
||||||
/** The registrar that this poll message will be delivered to. */
|
/** The registrar that this poll message will be delivered to. */
|
||||||
@Index
|
@Index
|
||||||
|
@ -134,7 +134,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||||
|
|
||||||
@Ignore Long hostHistoryRevisionId;
|
@Ignore Long hostHistoryRevisionId;
|
||||||
|
|
||||||
public Key<HistoryEntry> getParentKey() {
|
public Key<? extends HistoryEntry> getParentKey() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||||
return thisCastToDerived();
|
return thisCastToDerived();
|
||||||
}
|
}
|
||||||
|
|
||||||
public B setParentKey(Key<HistoryEntry> parentKey) {
|
public B setParentKey(Key<? extends HistoryEntry> parentKey) {
|
||||||
getInstance().parent = parentKey;
|
getInstance().parent = parentKey;
|
||||||
return thisCastToDerived();
|
return thisCastToDerived();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import google.registry.model.contact.ContactResource;
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
import google.registry.persistence.VKey;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,14 +79,14 @@ public class PollMessageExternalKeyConverter {
|
||||||
/**
|
/**
|
||||||
* Returns an Objectify Key to a PollMessage corresponding with the external ID.
|
* Returns an Objectify Key to a PollMessage corresponding with the external ID.
|
||||||
*
|
*
|
||||||
* <p>Note that the year field that is included at the end of the poll message isn't actually
|
* <p>Note that the year field that is included at the end of the poll message isn't actually used
|
||||||
* used for anything; it exists solely to create unique externally visible IDs for autorenews. We
|
* for anything; it exists solely to create unique externally visible IDs for autorenews. We thus
|
||||||
* thus ignore it (for now) for backwards compatibility reasons, so that registrars can still ACK
|
* ignore it (for now) for backwards compatibility reasons, so that registrars can still ACK
|
||||||
* existing poll message IDs they may have lying around.
|
* existing poll message IDs they may have lying around.
|
||||||
*
|
*
|
||||||
* @throws PollMessageExternalKeyParseException if the external key has an invalid format.
|
* @throws PollMessageExternalKeyParseException if the external key has an invalid format.
|
||||||
*/
|
*/
|
||||||
public static Key<PollMessage> parsePollMessageExternalId(String externalKey) {
|
public static VKey<PollMessage> parsePollMessageExternalId(String externalKey) {
|
||||||
List<String> idComponents = Splitter.on('-').splitToList(externalKey);
|
List<String> idComponents = Splitter.on('-').splitToList(externalKey);
|
||||||
if (idComponents.size() != 6) {
|
if (idComponents.size() != 6) {
|
||||||
throw new PollMessageExternalKeyParseException();
|
throw new PollMessageExternalKeyParseException();
|
||||||
|
@ -96,16 +97,17 @@ public class PollMessageExternalKeyConverter {
|
||||||
if (resourceClazz == null) {
|
if (resourceClazz == null) {
|
||||||
throw new PollMessageExternalKeyParseException();
|
throw new PollMessageExternalKeyParseException();
|
||||||
}
|
}
|
||||||
return Key.create(
|
return VKey.from(
|
||||||
Key.create(
|
Key.create(
|
||||||
Key.create(
|
Key.create(
|
||||||
null,
|
Key.create(
|
||||||
resourceClazz,
|
null,
|
||||||
String.format("%s-%s", idComponents.get(1), idComponents.get(2))),
|
resourceClazz,
|
||||||
HistoryEntry.class,
|
String.format("%s-%s", idComponents.get(1), idComponents.get(2))),
|
||||||
Long.parseLong(idComponents.get(3))),
|
HistoryEntry.class,
|
||||||
PollMessage.class,
|
Long.parseLong(idComponents.get(3))),
|
||||||
Long.parseLong(idComponents.get(4)));
|
PollMessage.class,
|
||||||
|
Long.parseLong(idComponents.get(4))));
|
||||||
// Note that idComponents.get(5) is entirely ignored; we never use the year field internally.
|
// Note that idComponents.get(5) is entirely ignored; we never use the year field internally.
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new PollMessageExternalKeyParseException();
|
throw new PollMessageExternalKeyParseException();
|
||||||
|
|
|
@ -15,16 +15,16 @@
|
||||||
package google.registry.tools;
|
package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
import static google.registry.flows.poll.PollFlowUtils.SQL_POLL_MESSAGE_QUERY;
|
||||||
import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery;
|
import static google.registry.flows.poll.PollFlowUtils.datastorePollMessageQuery;
|
||||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
import static google.registry.model.poll.PollMessageExternalKeyConverter.makePollMessageExternalId;
|
import static google.registry.model.poll.PollMessageExternalKeyConverter.makePollMessageExternalId;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.Parameters;
|
import com.beust.jcommander.Parameters;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import com.googlecode.objectify.cmd.QueryKeys;
|
import com.googlecode.objectify.cmd.QueryKeys;
|
||||||
|
@ -35,6 +35,7 @@ import google.registry.model.poll.PollMessage.OneTime;
|
||||||
import google.registry.util.Clock;
|
import google.registry.util.Clock;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to acknowledge one-time poll messages for a registrar.
|
* Command to acknowledge one-time poll messages for a registrar.
|
||||||
|
@ -60,11 +61,14 @@ import javax.inject.Inject;
|
||||||
@Parameters(separators = " =", commandDescription = "Acknowledge one-time poll messages.")
|
@Parameters(separators = " =", commandDescription = "Acknowledge one-time poll messages.")
|
||||||
final class AckPollMessagesCommand implements CommandWithRemoteApi {
|
final class AckPollMessagesCommand implements CommandWithRemoteApi {
|
||||||
|
|
||||||
|
private static final String SQL_POLL_MESSAGE_QUERY_BY_MESSAGE =
|
||||||
|
"FROM PollMessage WHERE clientId = :registrarId AND eventTime <= :now AND msg LIKE :msg"
|
||||||
|
+ " ORDER BY eventTime ASC";
|
||||||
|
|
||||||
@Parameter(
|
@Parameter(
|
||||||
names = {"-c", "--client"},
|
names = {"-c", "--client"},
|
||||||
description = "Client identifier of the registrar whose poll messages should be ACKed",
|
description = "Client identifier of the registrar whose poll messages should be ACKed",
|
||||||
required = true
|
required = true)
|
||||||
)
|
|
||||||
private String clientId;
|
private String clientId;
|
||||||
|
|
||||||
@Parameter(
|
@Parameter(
|
||||||
|
@ -84,28 +88,72 @@ final class AckPollMessagesCommand implements CommandWithRemoteApi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
QueryKeys<PollMessage> query = getPollMessagesQuery(clientId, clock.nowUtc()).keys();
|
if (tm().isOfy()) {
|
||||||
// TODO(b/160325686): Remove the batch logic after db migration.
|
ackPollMessagesDatastore();
|
||||||
for (List<Key<PollMessage>> keys : Iterables.partition(query, BATCH_SIZE)) {
|
} else {
|
||||||
tm().transact(
|
ackPollMessagesSql();
|
||||||
() -> {
|
|
||||||
// Load poll messages and filter to just those of interest.
|
|
||||||
ImmutableList<PollMessage> pollMessages =
|
|
||||||
ofy().load().keys(keys).values().stream()
|
|
||||||
.filter(pm -> isNullOrEmpty(message) || pm.getMsg().contains(message))
|
|
||||||
.collect(toImmutableList());
|
|
||||||
if (!dryRun) {
|
|
||||||
pollMessages.forEach(PollFlowUtils::ackPollMessage);
|
|
||||||
}
|
|
||||||
pollMessages.forEach(
|
|
||||||
pm ->
|
|
||||||
System.out.println(
|
|
||||||
Joiner.on(',')
|
|
||||||
.join(
|
|
||||||
makePollMessageExternalId(pm),
|
|
||||||
pm.getEventTime(),
|
|
||||||
pm.getMsg())));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and acks the matching poll messages from Datastore.
|
||||||
|
*
|
||||||
|
* <p>We have to first load the poll message keys then batch-load the objects themselves due to
|
||||||
|
* the Datastore size limits.
|
||||||
|
*/
|
||||||
|
private void ackPollMessagesDatastore() {
|
||||||
|
QueryKeys<PollMessage> query = datastorePollMessageQuery(clientId, clock.nowUtc()).keys();
|
||||||
|
for (List<Key<PollMessage>> keys : Iterables.partition(query, BATCH_SIZE)) {
|
||||||
|
tm().transact(
|
||||||
|
() ->
|
||||||
|
// Load poll messages and filter to just those of interest.
|
||||||
|
ofy().load().keys(keys).values().stream()
|
||||||
|
.filter(pm -> isNullOrEmpty(message) || pm.getMsg().contains(message))
|
||||||
|
.forEach(this::actOnPollMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads and acks all matching poll messages from SQL in one transaction. */
|
||||||
|
private void ackPollMessagesSql() {
|
||||||
|
jpaTm()
|
||||||
|
.transact(
|
||||||
|
() -> {
|
||||||
|
TypedQuery<PollMessage> typedQuery;
|
||||||
|
if (isNullOrEmpty(message)) {
|
||||||
|
typedQuery = jpaTm().query(SQL_POLL_MESSAGE_QUERY, PollMessage.class);
|
||||||
|
} else {
|
||||||
|
typedQuery =
|
||||||
|
jpaTm()
|
||||||
|
.query(SQL_POLL_MESSAGE_QUERY_BY_MESSAGE, PollMessage.class)
|
||||||
|
.setParameter("msg", "%" + message + "%");
|
||||||
|
}
|
||||||
|
typedQuery
|
||||||
|
.setParameter("registrarId", clientId)
|
||||||
|
.setParameter("now", clock.nowUtc())
|
||||||
|
.getResultStream()
|
||||||
|
// Detach it so that we can print out the old, non-acked version
|
||||||
|
// (for autorenews, acking changes the next event time)
|
||||||
|
.peek(jpaTm().getEntityManager()::detach)
|
||||||
|
.forEach(this::actOnPollMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acks the poll message if not running in dry-run mode, prints regardless.
|
||||||
|
*
|
||||||
|
* <p>This is a separate function because the processing of poll messages is transactionally
|
||||||
|
* different between the Datastore and SQL implementations. Datastore must process the messages in
|
||||||
|
* batches, whereas we can load all messages from SQL in one transaction.
|
||||||
|
*/
|
||||||
|
private void actOnPollMessage(PollMessage pollMessage) {
|
||||||
|
if (!dryRun) {
|
||||||
|
PollFlowUtils.ackPollMessage(pollMessage);
|
||||||
|
}
|
||||||
|
System.out.println(
|
||||||
|
Joiner.on(',')
|
||||||
|
.join(
|
||||||
|
makePollMessageExternalId(pollMessage),
|
||||||
|
pollMessage.getEventTime(),
|
||||||
|
pollMessage.getMsg()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,16 @@ import google.registry.flows.poll.PollAckFlow.NotAuthorizedToAckMessageException
|
||||||
import google.registry.model.contact.ContactResource;
|
import google.registry.model.contact.ContactResource;
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
|
import google.registry.testing.DualDatabaseTest;
|
||||||
import google.registry.testing.ReplayExtension;
|
import google.registry.testing.ReplayExtension;
|
||||||
|
import google.registry.testing.TestOfyAndSql;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Order;
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
/** Unit tests for {@link PollAckFlow}. */
|
/** Unit tests for {@link PollAckFlow}. */
|
||||||
|
@DualDatabaseTest
|
||||||
class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
|
|
||||||
@Order(value = Order.DEFAULT - 2)
|
@Order(value = Order.DEFAULT - 2)
|
||||||
|
@ -89,13 +91,13 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testDryRun() throws Exception {
|
void testDryRun() throws Exception {
|
||||||
persistOneTimePollMessage(MESSAGE_ID);
|
persistOneTimePollMessage(MESSAGE_ID);
|
||||||
dryRunFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
dryRunFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_contactPollMessage() throws Exception {
|
void testSuccess_contactPollMessage() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3-2011"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3-2011"));
|
||||||
persistResource(
|
persistResource(
|
||||||
|
@ -110,7 +112,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_contactPollMessage_withIncorrectYearField() throws Exception {
|
void testFailure_contactPollMessage_withIncorrectYearField() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3-1999"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3-1999"));
|
||||||
persistResource(
|
persistResource(
|
||||||
|
@ -125,14 +127,14 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
assertThrows(MessageDoesNotExistException.class, this::runFlow);
|
assertThrows(MessageDoesNotExistException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_messageOnContactResource() throws Exception {
|
void testSuccess_messageOnContactResource() throws Exception {
|
||||||
persistOneTimePollMessage(MESSAGE_ID);
|
persistOneTimePollMessage(MESSAGE_ID);
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_recentActiveAutorenew() throws Exception {
|
void testSuccess_recentActiveAutorenew() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2010"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2010"));
|
||||||
persistAutorenewPollMessage(clock.nowUtc().minusMonths(6), END_OF_TIME);
|
persistAutorenewPollMessage(clock.nowUtc().minusMonths(6), END_OF_TIME);
|
||||||
|
@ -140,7 +142,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_oldActiveAutorenew() throws Exception {
|
void testSuccess_oldActiveAutorenew() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2009"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2009"));
|
||||||
persistAutorenewPollMessage(clock.nowUtc().minusYears(2), END_OF_TIME);
|
persistAutorenewPollMessage(clock.nowUtc().minusYears(2), END_OF_TIME);
|
||||||
|
@ -156,7 +158,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2009", "COUNT", "4")));
|
ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2009", "COUNT", "4")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_oldInactiveAutorenew() throws Exception {
|
void testSuccess_oldInactiveAutorenew() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2010"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2010"));
|
||||||
persistAutorenewPollMessage(clock.nowUtc().minusMonths(6), clock.nowUtc());
|
persistAutorenewPollMessage(clock.nowUtc().minusMonths(6), clock.nowUtc());
|
||||||
|
@ -164,7 +166,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_ack_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_moreMessages() throws Exception {
|
void testSuccess_moreMessages() throws Exception {
|
||||||
// Create five messages to be queued for retrieval, one of which will be acked.
|
// Create five messages to be queued for retrieval, one of which will be acked.
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
|
@ -177,28 +179,28 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2011", "COUNT", "4")));
|
ImmutableMap.of("MSGID", "1-3-EXAMPLE-4-3-2011", "COUNT", "4")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_noSuchMessage() throws Exception {
|
void testFailure_noSuchMessage() throws Exception {
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
Exception e = assertThrows(MessageDoesNotExistException.class, this::runFlow);
|
Exception e = assertThrows(MessageDoesNotExistException.class, this::runFlow);
|
||||||
assertThat(e).hasMessageThat().contains(String.format("(1-3-EXAMPLE-4-%d-2011)", MESSAGE_ID));
|
assertThat(e).hasMessageThat().contains(String.format("(1-3-EXAMPLE-4-%d-2011)", MESSAGE_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_invalidId_tooFewComponents() throws Exception {
|
void testFailure_invalidId_tooFewComponents() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-2-3"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "1-2-3"));
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_invalidId_tooManyComponents() throws Exception {
|
void testFailure_invalidId_tooManyComponents() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3-1999-2007"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3-1999-2007"));
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_contactPollMessage_withMissingYearField() throws Exception {
|
void testFailure_contactPollMessage_withMissingYearField() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "2-2-ROID-4-3"));
|
||||||
persistResource(
|
persistResource(
|
||||||
|
@ -213,28 +215,28 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_invalidId_stringInsteadOfNumeric() throws Exception {
|
void testFailure_invalidId_stringInsteadOfNumeric() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "ABC-12345"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "ABC-12345"));
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_invalidEppResourceClassId() throws Exception {
|
void testFailure_invalidEppResourceClassId() throws Exception {
|
||||||
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "999-1-1-1"));
|
setEppInput("poll_ack.xml", ImmutableMap.of("MSGID", "999-1-1-1"));
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
assertThrows(InvalidMessageIdException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_missingId() throws Exception {
|
void testFailure_missingId() throws Exception {
|
||||||
setEppInput("poll_ack_missing_id.xml");
|
setEppInput("poll_ack_missing_id.xml");
|
||||||
assertTransactionalFlow(true);
|
assertTransactionalFlow(true);
|
||||||
assertThrows(MissingMessageIdException.class, this::runFlow);
|
assertThrows(MissingMessageIdException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_differentRegistrar() throws Exception {
|
void testFailure_differentRegistrar() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
@ -248,7 +250,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||||
assertThrows(NotAuthorizedToAckMessageException.class, this::runFlow);
|
assertThrows(NotAuthorizedToAckMessageException.class, this::runFlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_messageInFuture() throws Exception {
|
void testFailure_messageInFuture() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
|
|
@ -38,14 +38,16 @@ import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
||||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||||
import google.registry.model.transfer.TransferStatus;
|
import google.registry.model.transfer.TransferStatus;
|
||||||
|
import google.registry.testing.DualDatabaseTest;
|
||||||
import google.registry.testing.ReplayExtension;
|
import google.registry.testing.ReplayExtension;
|
||||||
import google.registry.testing.SetClockExtension;
|
import google.registry.testing.SetClockExtension;
|
||||||
|
import google.registry.testing.TestOfyAndSql;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Order;
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
/** Unit tests for {@link PollRequestFlow}. */
|
/** Unit tests for {@link PollRequestFlow}. */
|
||||||
|
@DualDatabaseTest
|
||||||
class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
|
|
||||||
@Order(value = Order.DEFAULT - 3)
|
@Order(value = Order.DEFAULT - 3)
|
||||||
|
@ -92,14 +94,14 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_domainTransferApproved() throws Exception {
|
void testSuccess_domainTransferApproved() throws Exception {
|
||||||
persistPendingTransferPollMessage();
|
persistPendingTransferPollMessage();
|
||||||
assertTransactionalFlow(false);
|
assertTransactionalFlow(false);
|
||||||
runFlowAssertResponse(loadFile("poll_response_domain_transfer.xml"));
|
runFlowAssertResponse(loadFile("poll_response_domain_transfer.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_clTridNotSpecified() throws Exception {
|
void testSuccess_clTridNotSpecified() throws Exception {
|
||||||
setEppInput("poll_no_cltrid.xml");
|
setEppInput("poll_no_cltrid.xml");
|
||||||
persistPendingTransferPollMessage();
|
persistPendingTransferPollMessage();
|
||||||
|
@ -107,7 +109,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_domain_transfer_no_cltrid.xml"));
|
runFlowAssertResponse(loadFile("poll_response_domain_transfer_no_cltrid.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_contactTransferPending() throws Exception {
|
void testSuccess_contactTransferPending() throws Exception {
|
||||||
setClientIdForFlow("TheRegistrar");
|
setClientIdForFlow("TheRegistrar");
|
||||||
persistResource(
|
persistResource(
|
||||||
|
@ -130,7 +132,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_contact_transfer.xml"));
|
runFlowAssertResponse(loadFile("poll_response_contact_transfer.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_domainPendingActionComplete() throws Exception {
|
void testSuccess_domainPendingActionComplete() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
@ -145,7 +147,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_domain_pending_notification.xml"));
|
runFlowAssertResponse(loadFile("poll_response_domain_pending_notification.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_domainAutorenewMessage() throws Exception {
|
void testSuccess_domainAutorenewMessage() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.Autorenew.Builder()
|
new PollMessage.Autorenew.Builder()
|
||||||
|
@ -159,12 +161,12 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_autorenew.xml"));
|
runFlowAssertResponse(loadFile("poll_response_autorenew.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_empty() throws Exception {
|
void testSuccess_empty() throws Exception {
|
||||||
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_wrongRegistrar() throws Exception {
|
void testSuccess_wrongRegistrar() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
@ -176,7 +178,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_futurePollMessage() throws Exception {
|
void testSuccess_futurePollMessage() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
@ -188,7 +190,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_futureAutorenew() throws Exception {
|
void testSuccess_futureAutorenew() throws Exception {
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.Autorenew.Builder()
|
new PollMessage.Autorenew.Builder()
|
||||||
|
@ -202,7 +204,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
runFlowAssertResponse(loadFile("poll_response_empty.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_contactDelete() throws Exception {
|
void testSuccess_contactDelete() throws Exception {
|
||||||
// Contact delete poll messages do not have any response data, so ensure that no
|
// Contact delete poll messages do not have any response data, so ensure that no
|
||||||
// response data block is produced in the poll message.
|
// response data block is produced in the poll message.
|
||||||
|
@ -223,7 +225,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_contact_delete.xml"));
|
runFlowAssertResponse(loadFile("poll_response_contact_delete.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_hostDelete() throws Exception {
|
void testSuccess_hostDelete() throws Exception {
|
||||||
// Host delete poll messages do not have any response data, so ensure that no
|
// Host delete poll messages do not have any response data, so ensure that no
|
||||||
// response data block is produced in the poll message.
|
// response data block is produced in the poll message.
|
||||||
|
@ -245,7 +247,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||||
runFlowAssertResponse(loadFile("poll_response_host_delete.xml"));
|
runFlowAssertResponse(loadFile("poll_response_host_delete.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_messageIdProvided() throws Exception {
|
void testFailure_messageIdProvided() throws Exception {
|
||||||
setEppInput("poll_with_id.xml");
|
setEppInput("poll_with_id.xml");
|
||||||
assertTransactionalFlow(false);
|
assertTransactionalFlow(false);
|
||||||
|
|
|
@ -25,21 +25,25 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import com.googlecode.objectify.Key;
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.domain.Period;
|
import google.registry.model.domain.Period;
|
||||||
import google.registry.model.eppcommon.Trid;
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.ofy.Ofy;
|
import google.registry.model.ofy.Ofy;
|
||||||
import google.registry.model.poll.PollMessageExternalKeyConverter.PollMessageExternalKeyParseException;
|
import google.registry.model.poll.PollMessageExternalKeyConverter.PollMessageExternalKeyParseException;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
import google.registry.persistence.VKey;
|
||||||
import google.registry.testing.AppEngineExtension;
|
import google.registry.testing.AppEngineExtension;
|
||||||
|
import google.registry.testing.DatabaseHelper;
|
||||||
|
import google.registry.testing.DualDatabaseTest;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.InjectExtension;
|
import google.registry.testing.InjectExtension;
|
||||||
|
import google.registry.testing.TestOfyAndSql;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
/** Unit tests for {@link PollMessageExternalKeyConverter}. */
|
/** Unit tests for {@link PollMessageExternalKeyConverter}. */
|
||||||
|
@DualDatabaseTest
|
||||||
public class PollMessageExternalKeyConverterTest {
|
public class PollMessageExternalKeyConverterTest {
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
|
@ -55,21 +59,23 @@ public class PollMessageExternalKeyConverterTest {
|
||||||
void beforeEach() {
|
void beforeEach() {
|
||||||
inject.setStaticField(Ofy.class, "clock", clock);
|
inject.setStaticField(Ofy.class, "clock", clock);
|
||||||
createTld("foobar");
|
createTld("foobar");
|
||||||
historyEntry = persistResource(new HistoryEntry.Builder()
|
historyEntry =
|
||||||
.setParent(persistActiveDomain("foo.foobar"))
|
persistResource(
|
||||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
new DomainHistory.Builder()
|
||||||
.setPeriod(Period.create(1, Period.Unit.YEARS))
|
.setParent(persistActiveDomain("foo.foobar"))
|
||||||
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||||
.setModificationTime(clock.nowUtc())
|
.setPeriod(Period.create(1, Period.Unit.YEARS))
|
||||||
.setClientId("foo")
|
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
||||||
.setTrid(Trid.create("ABC-123", "server-trid"))
|
.setModificationTime(clock.nowUtc())
|
||||||
.setBySuperuser(false)
|
.setClientId("TheRegistrar")
|
||||||
.setReason("reason")
|
.setTrid(Trid.create("ABC-123", "server-trid"))
|
||||||
.setRequestedByRegistrar(false)
|
.setBySuperuser(false)
|
||||||
.build());
|
.setReason("reason")
|
||||||
|
.setRequestedByRegistrar(false)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_domain() {
|
void testSuccess_domain() {
|
||||||
PollMessage.OneTime pollMessage =
|
PollMessage.OneTime pollMessage =
|
||||||
persistResource(
|
persistResource(
|
||||||
|
@ -80,14 +86,14 @@ public class PollMessageExternalKeyConverterTest {
|
||||||
.setParent(historyEntry)
|
.setParent(historyEntry)
|
||||||
.build());
|
.build());
|
||||||
assertThat(makePollMessageExternalId(pollMessage)).isEqualTo("1-2-FOOBAR-4-5-2007");
|
assertThat(makePollMessageExternalId(pollMessage)).isEqualTo("1-2-FOOBAR-4-5-2007");
|
||||||
assertThat(parsePollMessageExternalId("1-2-FOOBAR-4-5-2007"))
|
assertVKeysEqual(parsePollMessageExternalId("1-2-FOOBAR-4-5-2007"), pollMessage.createVKey());
|
||||||
.isEqualTo(Key.create(pollMessage));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_contact() {
|
void testSuccess_contact() {
|
||||||
historyEntry =
|
historyEntry =
|
||||||
persistResource(historyEntry.asBuilder().setParent(persistActiveContact("tim")).build());
|
persistResource(
|
||||||
|
DatabaseHelper.createHistoryEntryForEppResource(persistActiveContact("tim")));
|
||||||
PollMessage.OneTime pollMessage =
|
PollMessage.OneTime pollMessage =
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
@ -96,14 +102,15 @@ public class PollMessageExternalKeyConverterTest {
|
||||||
.setMsg("Test poll message")
|
.setMsg("Test poll message")
|
||||||
.setParent(historyEntry)
|
.setParent(historyEntry)
|
||||||
.build());
|
.build());
|
||||||
assertThat(makePollMessageExternalId(pollMessage)).isEqualTo("2-5-ROID-4-6-2007");
|
assertThat(makePollMessageExternalId(pollMessage)).isEqualTo("2-5-ROID-6-7-2007");
|
||||||
assertThat(parsePollMessageExternalId("2-5-ROID-4-6-2007")).isEqualTo(Key.create(pollMessage));
|
assertVKeysEqual(parsePollMessageExternalId("2-5-ROID-6-7-2007"), pollMessage.createVKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_host() {
|
void testSuccess_host() {
|
||||||
historyEntry =
|
historyEntry =
|
||||||
persistResource(historyEntry.asBuilder().setParent(persistActiveHost("time.zyx")).build());
|
persistResource(
|
||||||
|
DatabaseHelper.createHistoryEntryForEppResource(persistActiveHost("time.xyz")));
|
||||||
PollMessage.OneTime pollMessage =
|
PollMessage.OneTime pollMessage =
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
|
@ -112,18 +119,18 @@ public class PollMessageExternalKeyConverterTest {
|
||||||
.setMsg("Test poll message")
|
.setMsg("Test poll message")
|
||||||
.setParent(historyEntry)
|
.setParent(historyEntry)
|
||||||
.build());
|
.build());
|
||||||
assertThat(makePollMessageExternalId(pollMessage)).isEqualTo("3-5-ROID-4-6-2007");
|
assertThat(makePollMessageExternalId(pollMessage)).isEqualTo("3-5-ROID-6-7-2007");
|
||||||
assertThat(parsePollMessageExternalId("3-5-ROID-4-6-2007")).isEqualTo(Key.create(pollMessage));
|
assertVKeysEqual(parsePollMessageExternalId("3-5-ROID-6-7-2007"), pollMessage.createVKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_missingYearField() {
|
void testFailure_missingYearField() {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
PollMessageExternalKeyParseException.class,
|
PollMessageExternalKeyParseException.class,
|
||||||
() -> parsePollMessageExternalId("1-2-FOOBAR-4-5"));
|
() -> parsePollMessageExternalId("1-2-FOOBAR-4-5"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_invalidEppResourceTypeId() {
|
void testFailure_invalidEppResourceTypeId() {
|
||||||
// Populate the testdata correctly as for 1-2-FOOBAR-4-5 so we know that the only thing that
|
// Populate the testdata correctly as for 1-2-FOOBAR-4-5 so we know that the only thing that
|
||||||
// is wrong here is the EppResourceTypeId.
|
// is wrong here is the EppResourceTypeId.
|
||||||
|
@ -133,24 +140,36 @@ public class PollMessageExternalKeyConverterTest {
|
||||||
() -> parsePollMessageExternalId("4-2-FOOBAR-4-5-2007"));
|
() -> parsePollMessageExternalId("4-2-FOOBAR-4-5-2007"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_tooFewComponentParts() {
|
void testFailure_tooFewComponentParts() {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
PollMessageExternalKeyParseException.class,
|
PollMessageExternalKeyParseException.class,
|
||||||
() -> parsePollMessageExternalId("1-3-EXAMPLE"));
|
() -> parsePollMessageExternalId("1-3-EXAMPLE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_tooManyComponentParts() {
|
void testFailure_tooManyComponentParts() {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
PollMessageExternalKeyParseException.class,
|
PollMessageExternalKeyParseException.class,
|
||||||
() -> parsePollMessageExternalId("1-3-EXAMPLE-4-5-2007-2009"));
|
() -> parsePollMessageExternalId("1-3-EXAMPLE-4-5-2007-2009"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testFailure_nonNumericIds() {
|
void testFailure_nonNumericIds() {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
PollMessageExternalKeyParseException.class,
|
PollMessageExternalKeyParseException.class,
|
||||||
() -> parsePollMessageExternalId("A-B-FOOBAR-D-E-F"));
|
() -> parsePollMessageExternalId("A-B-FOOBAR-D-E-F"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We may have VKeys of slightly varying types, e.g. VKey<PollMessage> (superclass) and
|
||||||
|
// VKey<PollMessage.OneTime> (subclass). We should treat these as equal since the DB does.
|
||||||
|
private static void assertVKeysEqual(
|
||||||
|
VKey<? extends PollMessage> one, VKey<? extends PollMessage> two) {
|
||||||
|
assertThat(
|
||||||
|
one.getKind().isAssignableFrom(two.getKind())
|
||||||
|
|| two.getKind().isAssignableFrom(one.getKind()))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(one.getSqlKey()).isEqualTo(two.getSqlKey());
|
||||||
|
assertThat(one.getOfyKey()).isEqualTo(two.getOfyKey());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,7 @@ import google.registry.tmch.LordnTaskUtils;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -1208,6 +1209,7 @@ public class DatabaseHelper {
|
||||||
.setType(getHistoryEntryType(parentResource))
|
.setType(getHistoryEntryType(parentResource))
|
||||||
.setModificationTime(DateTime.now(DateTimeZone.UTC))
|
.setModificationTime(DateTime.now(DateTimeZone.UTC))
|
||||||
.setParent(parentResource)
|
.setParent(parentResource)
|
||||||
|
.setClientId(parentResource.getPersistedCurrentSponsorClientId())
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1329,5 +1331,19 @@ public class DatabaseHelper {
|
||||||
return transactIfJpaTm(() -> tm().loadAllOf(clazz));
|
return transactIfJpaTm(() -> tm().loadAllOf(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the set of entities by their keys from the DB.
|
||||||
|
*
|
||||||
|
* <p>If the transaction manager is Cloud SQL, then this creates an inner wrapping transaction for
|
||||||
|
* convenience, so you don't need to wrap it in a transaction at the callsite.
|
||||||
|
*
|
||||||
|
* <p>Nonexistent keys / entities are absent from the resulting map, but no {@link
|
||||||
|
* NoSuchElementException} will be thrown.
|
||||||
|
*/
|
||||||
|
public static <T> ImmutableMap<VKey<? extends T>, T> loadByKeysIfPresent(
|
||||||
|
Iterable<? extends VKey<? extends T>> keys) {
|
||||||
|
return transactIfJpaTm(() -> tm().loadByKeysIfPresent(keys));
|
||||||
|
}
|
||||||
|
|
||||||
private DatabaseHelper() {}
|
private DatabaseHelper() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,39 +16,59 @@ package google.registry.tools;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.testing.DatabaseHelper.createTld;
|
||||||
|
import static google.registry.testing.DatabaseHelper.loadByKeys;
|
||||||
|
import static google.registry.testing.DatabaseHelper.loadByKeysIfPresent;
|
||||||
|
import static google.registry.testing.DatabaseHelper.newDomainBase;
|
||||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
|
import google.registry.model.domain.DomainHistory;
|
||||||
import google.registry.model.ofy.Ofy;
|
import google.registry.model.ofy.Ofy;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.poll.PollMessage.Autorenew;
|
import google.registry.model.poll.PollMessage.Autorenew;
|
||||||
import google.registry.model.poll.PollMessage.OneTime;
|
import google.registry.model.poll.PollMessage.OneTime;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.persistence.VKey;
|
import google.registry.persistence.VKey;
|
||||||
|
import google.registry.testing.DualDatabaseTest;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.InjectExtension;
|
import google.registry.testing.InjectExtension;
|
||||||
|
import google.registry.testing.TestOfyAndSql;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
/** Unit tests for {@link AckPollMessagesCommand}. */
|
/** Unit tests for {@link AckPollMessagesCommand}. */
|
||||||
|
@DualDatabaseTest
|
||||||
public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesCommand> {
|
public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesCommand> {
|
||||||
|
|
||||||
private FakeClock clock = new FakeClock(DateTime.parse("2015-02-04T08:16:32.064Z"));
|
private FakeClock clock = new FakeClock(DateTime.parse("2015-02-04T08:16:32.064Z"));
|
||||||
|
|
||||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||||
|
|
||||||
|
private DomainHistory domainHistory;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
final void beforeEach() {
|
final void beforeEach() {
|
||||||
inject.setStaticField(Ofy.class, "clock", clock);
|
inject.setStaticField(Ofy.class, "clock", clock);
|
||||||
command.clock = clock;
|
command.clock = clock;
|
||||||
|
createTld("tld");
|
||||||
|
DomainBase domain = newDomainBase("example.tld").asBuilder().setRepoId("FSDGS-TLD").build();
|
||||||
|
persistResource(domain);
|
||||||
|
domainHistory =
|
||||||
|
persistResource(
|
||||||
|
new DomainHistory.Builder()
|
||||||
|
.setModificationTime(clock.nowUtc())
|
||||||
|
.setDomainRepoId(domain.getRepoId())
|
||||||
|
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||||
|
.setId(2406L)
|
||||||
|
.build());
|
||||||
|
clock.advanceOneMilli();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_doesntDeletePollMessagesInFuture() throws Exception {
|
void testSuccess_doesntDeletePollMessagesInFuture() throws Exception {
|
||||||
VKey<OneTime> pm1 =
|
VKey<OneTime> pm1 =
|
||||||
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
|
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
|
||||||
|
@ -60,7 +80,7 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
persistPollMessage(123L, DateTime.parse("2015-09-01T22:33:44Z"), "notme");
|
persistPollMessage(123L, DateTime.parse("2015-09-01T22:33:44Z"), "notme");
|
||||||
VKey<OneTime> pm4 = futurePollMessage.createVKey();
|
VKey<OneTime> pm4 = futurePollMessage.createVKey();
|
||||||
runCommand("-c", "TheRegistrar");
|
runCommand("-c", "TheRegistrar");
|
||||||
assertThat(tm().loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3, pm4)).values())
|
assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3, pm4)).values())
|
||||||
.containsExactly(futurePollMessage);
|
.containsExactly(futurePollMessage);
|
||||||
assertInStdout(
|
assertInStdout(
|
||||||
"1-FSDGS-TLD-2406-624-2013,2013-05-01T22:33:44.000Z,ninelives",
|
"1-FSDGS-TLD-2406-624-2013,2013-05-01T22:33:44.000Z,ninelives",
|
||||||
|
@ -69,7 +89,7 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
assertNotInStdout("1-FSDGS-TLD-2406-123-2015,2015-09-01T22:33:44.000Z,notme");
|
assertNotInStdout("1-FSDGS-TLD-2406-123-2015,2015-09-01T22:33:44.000Z,notme");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_resavesAutorenewPollMessages() throws Exception {
|
void testSuccess_resavesAutorenewPollMessages() throws Exception {
|
||||||
VKey<OneTime> pm1 =
|
VKey<OneTime> pm1 =
|
||||||
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
|
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
|
||||||
|
@ -78,10 +98,8 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
Autorenew autorenew =
|
Autorenew autorenew =
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.Autorenew.Builder()
|
new PollMessage.Autorenew.Builder()
|
||||||
.setId(624L)
|
.setId(625L)
|
||||||
.setParentKey(
|
.setParentKey(domainHistory.createVKey().getOfyKey())
|
||||||
Key.create(
|
|
||||||
Key.create(DomainBase.class, "AAFSGS-TLD"), HistoryEntry.class, 99406L))
|
|
||||||
.setEventTime(DateTime.parse("2011-04-15T22:33:44Z"))
|
.setEventTime(DateTime.parse("2011-04-15T22:33:44Z"))
|
||||||
.setClientId("TheRegistrar")
|
.setClientId("TheRegistrar")
|
||||||
.setMsg("autorenew")
|
.setMsg("autorenew")
|
||||||
|
@ -90,15 +108,15 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
autorenew.asBuilder().setEventTime(DateTime.parse("2012-04-15T22:33:44Z")).build();
|
autorenew.asBuilder().setEventTime(DateTime.parse("2012-04-15T22:33:44Z")).build();
|
||||||
VKey<Autorenew> pm3 = autorenew.createVKey();
|
VKey<Autorenew> pm3 = autorenew.createVKey();
|
||||||
runCommand("-c", "TheRegistrar");
|
runCommand("-c", "TheRegistrar");
|
||||||
assertThat(tm().loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3)).values())
|
assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3)).values())
|
||||||
.containsExactly(resaved);
|
.containsExactly(resaved);
|
||||||
assertInStdout(
|
assertInStdout(
|
||||||
"1-AAFSGS-TLD-99406-624-2011,2011-04-15T22:33:44.000Z,autorenew",
|
"1-FSDGS-TLD-2406-625-2011,2011-04-15T22:33:44.000Z,autorenew",
|
||||||
"1-FSDGS-TLD-2406-624-2013,2013-05-01T22:33:44.000Z,ninelives",
|
"1-FSDGS-TLD-2406-624-2013,2013-05-01T22:33:44.000Z,ninelives",
|
||||||
"1-FSDGS-TLD-2406-316-2014,2014-01-01T22:33:44.000Z,foobar");
|
"1-FSDGS-TLD-2406-316-2014,2014-01-01T22:33:44.000Z,foobar");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_deletesExpiredAutorenewPollMessages() throws Exception {
|
void testSuccess_deletesExpiredAutorenewPollMessages() throws Exception {
|
||||||
VKey<OneTime> pm1 =
|
VKey<OneTime> pm1 =
|
||||||
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
|
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar").createVKey();
|
||||||
|
@ -107,10 +125,8 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
Autorenew autorenew =
|
Autorenew autorenew =
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.Autorenew.Builder()
|
new PollMessage.Autorenew.Builder()
|
||||||
.setId(624L)
|
.setId(625L)
|
||||||
.setParentKey(
|
.setParentKey(domainHistory.createVKey().getOfyKey())
|
||||||
Key.create(
|
|
||||||
Key.create(DomainBase.class, "AAFSGS-TLD"), HistoryEntry.class, 99406L))
|
|
||||||
.setEventTime(DateTime.parse("2011-04-15T22:33:44Z"))
|
.setEventTime(DateTime.parse("2011-04-15T22:33:44Z"))
|
||||||
.setAutorenewEndTime(DateTime.parse("2012-01-01T22:33:44Z"))
|
.setAutorenewEndTime(DateTime.parse("2012-01-01T22:33:44Z"))
|
||||||
.setClientId("TheRegistrar")
|
.setClientId("TheRegistrar")
|
||||||
|
@ -118,14 +134,14 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
.build());
|
.build());
|
||||||
VKey<Autorenew> pm3 = autorenew.createVKey();
|
VKey<Autorenew> pm3 = autorenew.createVKey();
|
||||||
runCommand("-c", "TheRegistrar");
|
runCommand("-c", "TheRegistrar");
|
||||||
assertThat(tm().loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3))).isEmpty();
|
assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3))).isEmpty();
|
||||||
assertInStdout(
|
assertInStdout(
|
||||||
"1-AAFSGS-TLD-99406-624-2011,2011-04-15T22:33:44.000Z,autorenew",
|
"1-FSDGS-TLD-2406-625-2011,2011-04-15T22:33:44.000Z,autorenew",
|
||||||
"1-FSDGS-TLD-2406-624-2013,2013-05-01T22:33:44.000Z,ninelives",
|
"1-FSDGS-TLD-2406-624-2013,2013-05-01T22:33:44.000Z,ninelives",
|
||||||
"1-FSDGS-TLD-2406-316-2014,2014-01-01T22:33:44.000Z,foobar");
|
"1-FSDGS-TLD-2406-316-2014,2014-01-01T22:33:44.000Z,foobar");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_onlyDeletesPollMessagesMatchingMessage() throws Exception {
|
void testSuccess_onlyDeletesPollMessagesMatchingMessage() throws Exception {
|
||||||
VKey<OneTime> pm1 =
|
VKey<OneTime> pm1 =
|
||||||
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "food is good")
|
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "food is good")
|
||||||
|
@ -139,11 +155,11 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
persistPollMessage(123L, DateTime.parse("2015-09-01T22:33:44Z"), "time flies");
|
persistPollMessage(123L, DateTime.parse("2015-09-01T22:33:44Z"), "time flies");
|
||||||
VKey<OneTime> pm4 = notMatched2.createVKey();
|
VKey<OneTime> pm4 = notMatched2.createVKey();
|
||||||
runCommand("-c", "TheRegistrar", "-m", "food");
|
runCommand("-c", "TheRegistrar", "-m", "food");
|
||||||
assertThat(tm().loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3, pm4)).values())
|
assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3, pm4)).values())
|
||||||
.containsExactly(notMatched1, notMatched2);
|
.containsExactly(notMatched1, notMatched2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_onlyDeletesPollMessagesMatchingClientId() throws Exception {
|
void testSuccess_onlyDeletesPollMessagesMatchingClientId() throws Exception {
|
||||||
VKey<OneTime> pm1 =
|
VKey<OneTime> pm1 =
|
||||||
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "food is good")
|
persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "food is good")
|
||||||
|
@ -155,20 +171,18 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
persistResource(
|
persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
.setId(2474L)
|
.setId(2474L)
|
||||||
.setParentKey(
|
.setParentKey(domainHistory.createVKey().getOfyKey())
|
||||||
Key.create(
|
|
||||||
Key.create(DomainBase.class, "FSDGS-TLD"), HistoryEntry.class, 2406L))
|
|
||||||
.setClientId("NewRegistrar")
|
.setClientId("NewRegistrar")
|
||||||
.setEventTime(DateTime.parse("2013-06-01T22:33:44Z"))
|
.setEventTime(DateTime.parse("2013-06-01T22:33:44Z"))
|
||||||
.setMsg("baaaahh")
|
.setMsg("baaaahh")
|
||||||
.build());
|
.build());
|
||||||
VKey<OneTime> pm3 = notMatched.createVKey();
|
VKey<OneTime> pm3 = notMatched.createVKey();
|
||||||
runCommand("-c", "TheRegistrar");
|
runCommand("-c", "TheRegistrar");
|
||||||
assertThat(tm().loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3)).values())
|
assertThat(loadByKeysIfPresent(ImmutableList.of(pm1, pm2, pm3)).values())
|
||||||
.containsExactly(notMatched);
|
.containsExactly(notMatched);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestOfyAndSql
|
||||||
void testSuccess_dryRunDoesntDeleteAnything() throws Exception {
|
void testSuccess_dryRunDoesntDeleteAnything() throws Exception {
|
||||||
OneTime pm1 = persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar");
|
OneTime pm1 = persistPollMessage(316L, DateTime.parse("2014-01-01T22:33:44Z"), "foobar");
|
||||||
OneTime pm2 = persistPollMessage(624L, DateTime.parse("2013-05-01T22:33:44Z"), "ninelives");
|
OneTime pm2 = persistPollMessage(624L, DateTime.parse("2013-05-01T22:33:44Z"), "ninelives");
|
||||||
|
@ -176,20 +190,22 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||||
OneTime pm4 = persistPollMessage(123L, DateTime.parse("2015-09-01T22:33:44Z"), "notme");
|
OneTime pm4 = persistPollMessage(123L, DateTime.parse("2015-09-01T22:33:44Z"), "notme");
|
||||||
runCommand("-c", "TheRegistrar", "-d");
|
runCommand("-c", "TheRegistrar", "-d");
|
||||||
assertThat(
|
assertThat(
|
||||||
tm().loadByKeys(
|
loadByKeys(
|
||||||
ImmutableList.of(pm1, pm2, pm3, pm4).stream()
|
ImmutableList.of(pm1, pm2, pm3, pm4).stream()
|
||||||
.map(OneTime::createVKey)
|
.map(OneTime::createVKey)
|
||||||
.collect(toImmutableList()))
|
.collect(toImmutableList())))
|
||||||
.values())
|
|
||||||
.containsExactly(pm1, pm2, pm3, pm4);
|
.containsExactly(pm1, pm2, pm3, pm4);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OneTime persistPollMessage(long id, DateTime eventTime, String message) {
|
private OneTime persistPollMessage(long id, DateTime eventTime, String message) {
|
||||||
return persistResource(
|
return persistResource(
|
||||||
new PollMessage.OneTime.Builder()
|
new PollMessage.OneTime.Builder()
|
||||||
.setId(id)
|
.setId(id)
|
||||||
.setParentKey(
|
.setParentKey(
|
||||||
Key.create(Key.create(DomainBase.class, "FSDGS-TLD"), HistoryEntry.class, 2406L))
|
Key.create(
|
||||||
|
Key.create(DomainBase.class, "FSDGS-TLD"),
|
||||||
|
HistoryEntry.class,
|
||||||
|
domainHistory.getId()))
|
||||||
.setClientId("TheRegistrar")
|
.setClientId("TheRegistrar")
|
||||||
.setEventTime(eventTime)
|
.setEventTime(eventTime)
|
||||||
.setMsg(message)
|
.setMsg(message)
|
||||||
|
|
|
@ -510,14 +510,14 @@ class google.registry.model.poll.PendingActionNotificationResponse$NameOrId {
|
||||||
}
|
}
|
||||||
class google.registry.model.poll.PollMessage {
|
class google.registry.model.poll.PollMessage {
|
||||||
@Id java.lang.Long id;
|
@Id java.lang.Long id;
|
||||||
@Parent com.googlecode.objectify.Key<google.registry.model.reporting.HistoryEntry> parent;
|
@Parent com.googlecode.objectify.Key<? extends google.registry.model.reporting.HistoryEntry> parent;
|
||||||
java.lang.String clientId;
|
java.lang.String clientId;
|
||||||
java.lang.String msg;
|
java.lang.String msg;
|
||||||
org.joda.time.DateTime eventTime;
|
org.joda.time.DateTime eventTime;
|
||||||
}
|
}
|
||||||
class google.registry.model.poll.PollMessage$Autorenew {
|
class google.registry.model.poll.PollMessage$Autorenew {
|
||||||
@Id java.lang.Long id;
|
@Id java.lang.Long id;
|
||||||
@Parent com.googlecode.objectify.Key<google.registry.model.reporting.HistoryEntry> parent;
|
@Parent com.googlecode.objectify.Key<? extends google.registry.model.reporting.HistoryEntry> parent;
|
||||||
java.lang.String clientId;
|
java.lang.String clientId;
|
||||||
java.lang.String msg;
|
java.lang.String msg;
|
||||||
java.lang.String targetId;
|
java.lang.String targetId;
|
||||||
|
@ -526,7 +526,7 @@ class google.registry.model.poll.PollMessage$Autorenew {
|
||||||
}
|
}
|
||||||
class google.registry.model.poll.PollMessage$OneTime {
|
class google.registry.model.poll.PollMessage$OneTime {
|
||||||
@Id java.lang.Long id;
|
@Id java.lang.Long id;
|
||||||
@Parent com.googlecode.objectify.Key<google.registry.model.reporting.HistoryEntry> parent;
|
@Parent com.googlecode.objectify.Key<? extends google.registry.model.reporting.HistoryEntry> parent;
|
||||||
java.lang.String clientId;
|
java.lang.String clientId;
|
||||||
java.lang.String msg;
|
java.lang.String msg;
|
||||||
java.util.List<google.registry.model.poll.PendingActionNotificationResponse$ContactPendingActionNotificationResponse> contactPendingActionNotificationResponses;
|
java.util.List<google.registry.model.poll.PendingActionNotificationResponse$ContactPendingActionNotificationResponse> contactPendingActionNotificationResponses;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue