mirror of
https://github.com/google/nomulus.git
synced 2025-05-15 08:57:12 +02:00
Notify registrars of async contact/host deletions
We now send PendingActionNotificationResponses in our poll messages upon completion of an asynchronous contact or host deletion. This is part 1 of 2, which begins logging Trid in all enqueued Host/Contact deletion flows for use in batch deletions, and optionally consuming the resultant Trid info to emit a Host/ContactPendingActionNotifcationResponse. Part 2 will make this response emission non-optional, which will happen once the queue is cleared of all non-Trid containing tasks. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=153084197
This commit is contained in:
parent
478c7576c6
commit
5047d568de
12 changed files with 240 additions and 66 deletions
|
@ -23,9 +23,11 @@ import static com.googlecode.objectify.Key.getKind;
|
||||||
import static google.registry.flows.ResourceFlowUtils.createResolvedTransferData;
|
import static google.registry.flows.ResourceFlowUtils.createResolvedTransferData;
|
||||||
import static google.registry.flows.ResourceFlowUtils.handlePendingTransferOnDelete;
|
import static google.registry.flows.ResourceFlowUtils.handlePendingTransferOnDelete;
|
||||||
import static google.registry.flows.ResourceFlowUtils.updateForeignKeyIndexDeletionTime;
|
import static google.registry.flows.ResourceFlowUtils.updateForeignKeyIndexDeletionTime;
|
||||||
|
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_CLIENT_TRANSACTION_ID;
|
||||||
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_IS_SUPERUSER;
|
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_IS_SUPERUSER;
|
||||||
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_REQUESTING_CLIENT_ID;
|
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_REQUESTING_CLIENT_ID;
|
||||||
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_RESOURCE_KEY;
|
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_RESOURCE_KEY;
|
||||||
|
import static google.registry.flows.async.AsyncFlowEnqueuer.PARAM_SERVER_TRANSACTION_ID;
|
||||||
import static google.registry.flows.async.AsyncFlowEnqueuer.QUEUE_ASYNC_DELETE;
|
import static google.registry.flows.async.AsyncFlowEnqueuer.QUEUE_ASYNC_DELETE;
|
||||||
import static google.registry.model.EppResourceUtils.isActive;
|
import static google.registry.model.EppResourceUtils.isActive;
|
||||||
import static google.registry.model.EppResourceUtils.isDeleted;
|
import static google.registry.model.EppResourceUtils.isDeleted;
|
||||||
|
@ -68,7 +70,11 @@ import google.registry.model.annotations.ExternalMessagingName;
|
||||||
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.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
|
import google.registry.model.eppoutput.EppResponse.ResponseData;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
|
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
||||||
|
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.transfer.TransferStatus;
|
import google.registry.model.transfer.TransferStatus;
|
||||||
|
@ -318,6 +324,8 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||||
.setMsg(pollMessageText)
|
.setMsg(pollMessageText)
|
||||||
.setParent(historyEntry)
|
.setParent(historyEntry)
|
||||||
.setEventTime(now)
|
.setEventTime(now)
|
||||||
|
.setResponseData(
|
||||||
|
getPollMessageResponseData(deletionRequest, resource, deleteAllowed, now))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
EppResource resourceToSave;
|
EppResource resourceToSave;
|
||||||
|
@ -348,6 +356,35 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||||
deleteAllowed ? Type.DELETED : Type.NOT_DELETED, pollMessageText);
|
deleteAllowed ? Type.DELETED : Type.NOT_DELETED, pollMessageText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ImmutableList<? extends ResponseData> getPollMessageResponseData(
|
||||||
|
DeletionRequest deletionRequest,
|
||||||
|
EppResource resource,
|
||||||
|
boolean deleteAllowed,
|
||||||
|
DateTime now) {
|
||||||
|
Optional<String> clientTransactionId = deletionRequest.clientTransactionId();
|
||||||
|
Optional<String> serverTransactionId = deletionRequest.serverTransactionId();
|
||||||
|
// TODO(b/36402020): Make this unconditional, once older tasks enqueued without Trid data
|
||||||
|
// have been processed out of the queue.
|
||||||
|
checkState(
|
||||||
|
clientTransactionId.isPresent() == serverTransactionId.isPresent(),
|
||||||
|
"Found one part of TRID without the other!");
|
||||||
|
if (clientTransactionId.isPresent() && serverTransactionId.isPresent()) {
|
||||||
|
Trid trid = Trid.create(clientTransactionId.get(), serverTransactionId.get());
|
||||||
|
if (resource instanceof HostResource) {
|
||||||
|
return ImmutableList.of(
|
||||||
|
HostPendingActionNotificationResponse.create(
|
||||||
|
((HostResource) resource).getFullyQualifiedHostName(), deleteAllowed, trid, now));
|
||||||
|
} else if (resource instanceof ContactResource) {
|
||||||
|
return ImmutableList.of(
|
||||||
|
ContactPendingActionNotificationResponse.create(
|
||||||
|
((ContactResource) resource).getContactId(), deleteAllowed, trid, now));
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("EPP resource of unknown type " + Key.create(resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the proper history entry type for the delete operation, as a function of
|
* Determine the proper history entry type for the delete operation, as a function of
|
||||||
* whether or not the delete was successful.
|
* whether or not the delete was successful.
|
||||||
|
@ -403,6 +440,13 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||||
* the actual current owner of the resource).
|
* the actual current owner of the resource).
|
||||||
*/
|
*/
|
||||||
abstract String requestingClientId();
|
abstract String requestingClientId();
|
||||||
|
|
||||||
|
/** First half of TRID for the original request, split for serializability. */
|
||||||
|
abstract Optional<String> clientTransactionId();
|
||||||
|
|
||||||
|
/** Second half of TRID for the original request, split for serializability. */
|
||||||
|
abstract Optional<String> serverTransactionId();
|
||||||
|
|
||||||
abstract boolean isSuperuser();
|
abstract boolean isSuperuser();
|
||||||
abstract TaskHandle task();
|
abstract TaskHandle task();
|
||||||
|
|
||||||
|
@ -411,6 +455,8 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||||
abstract Builder setKey(Key<? extends EppResource> key);
|
abstract Builder setKey(Key<? extends EppResource> key);
|
||||||
abstract Builder setLastUpdateTime(DateTime lastUpdateTime);
|
abstract Builder setLastUpdateTime(DateTime lastUpdateTime);
|
||||||
abstract Builder setRequestingClientId(String requestingClientId);
|
abstract Builder setRequestingClientId(String requestingClientId);
|
||||||
|
abstract Builder setClientTransactionId(Optional<String> clientTransactionId);
|
||||||
|
abstract Builder setServerTransactionId(Optional<String> serverTransactionId);
|
||||||
abstract Builder setIsSuperuser(boolean isSuperuser);
|
abstract Builder setIsSuperuser(boolean isSuperuser);
|
||||||
abstract Builder setTask(TaskHandle task);
|
abstract Builder setTask(TaskHandle task);
|
||||||
abstract DeletionRequest build();
|
abstract DeletionRequest build();
|
||||||
|
@ -435,9 +481,17 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||||
new AutoValue_DeleteContactsAndHostsAction_DeletionRequest.Builder()
|
new AutoValue_DeleteContactsAndHostsAction_DeletionRequest.Builder()
|
||||||
.setKey(resourceKey)
|
.setKey(resourceKey)
|
||||||
.setLastUpdateTime(resource.getUpdateAutoTimestamp().getTimestamp())
|
.setLastUpdateTime(resource.getUpdateAutoTimestamp().getTimestamp())
|
||||||
.setRequestingClientId(checkNotNull(
|
.setRequestingClientId(
|
||||||
|
checkNotNull(
|
||||||
params.get(PARAM_REQUESTING_CLIENT_ID), "Requesting client id not specified"))
|
params.get(PARAM_REQUESTING_CLIENT_ID), "Requesting client id not specified"))
|
||||||
.setIsSuperuser(Boolean.valueOf(
|
.setClientTransactionId(
|
||||||
|
Optional.fromNullable(
|
||||||
|
params.get(PARAM_CLIENT_TRANSACTION_ID)))
|
||||||
|
.setServerTransactionId(
|
||||||
|
Optional.fromNullable(
|
||||||
|
params.get(PARAM_SERVER_TRANSACTION_ID)))
|
||||||
|
.setIsSuperuser(
|
||||||
|
Boolean.valueOf(
|
||||||
checkNotNull(params.get(PARAM_IS_SUPERUSER), "Is superuser not specified")))
|
checkNotNull(params.get(PARAM_IS_SUPERUSER), "Is superuser not specified")))
|
||||||
.setTask(task)
|
.setTask(task)
|
||||||
.build());
|
.build());
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
import google.registry.config.RegistryConfig.Config;
|
||||||
import google.registry.model.EppResource;
|
import google.registry.model.EppResource;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.util.FormattingLogger;
|
import google.registry.util.FormattingLogger;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.Retrier;
|
||||||
|
@ -36,6 +37,8 @@ public final class AsyncFlowEnqueuer {
|
||||||
/** The HTTP parameter names used by async flows. */
|
/** The HTTP parameter names used by async flows. */
|
||||||
public static final String PARAM_RESOURCE_KEY = "resourceKey";
|
public static final String PARAM_RESOURCE_KEY = "resourceKey";
|
||||||
public static final String PARAM_REQUESTING_CLIENT_ID = "requestingClientId";
|
public static final String PARAM_REQUESTING_CLIENT_ID = "requestingClientId";
|
||||||
|
public static final String PARAM_CLIENT_TRANSACTION_ID = "clientTransactionId";
|
||||||
|
public static final String PARAM_SERVER_TRANSACTION_ID = "serverTransactionId";
|
||||||
public static final String PARAM_IS_SUPERUSER = "isSuperuser";
|
public static final String PARAM_IS_SUPERUSER = "isSuperuser";
|
||||||
public static final String PARAM_HOST_KEY = "hostKey";
|
public static final String PARAM_HOST_KEY = "hostKey";
|
||||||
|
|
||||||
|
@ -70,17 +73,18 @@ public final class AsyncFlowEnqueuer {
|
||||||
|
|
||||||
/** Enqueues a task to asynchronously delete a contact or host, by key. */
|
/** Enqueues a task to asynchronously delete a contact or host, by key. */
|
||||||
public void enqueueAsyncDelete(
|
public void enqueueAsyncDelete(
|
||||||
EppResource resourceToDelete, String requestingClientId, boolean isSuperuser) {
|
EppResource resourceToDelete, String requestingClientId, Trid trid, boolean isSuperuser) {
|
||||||
Key<EppResource> resourceKey = Key.create(resourceToDelete);
|
Key<EppResource> resourceKey = Key.create(resourceToDelete);
|
||||||
logger.infofmt(
|
logger.infofmt(
|
||||||
"Enqueuing async deletion of %s on behalf of registrar %s.",
|
"Enqueuing async deletion of %s on behalf of registrar %s.",
|
||||||
resourceKey, requestingClientId);
|
resourceKey, requestingClientId);
|
||||||
TaskOptions task =
|
TaskOptions task =
|
||||||
TaskOptions.Builder
|
TaskOptions.Builder.withMethod(Method.PULL)
|
||||||
.withMethod(Method.PULL)
|
|
||||||
.countdownMillis(asyncDeleteDelay.getMillis())
|
.countdownMillis(asyncDeleteDelay.getMillis())
|
||||||
.param(PARAM_RESOURCE_KEY, resourceKey.getString())
|
.param(PARAM_RESOURCE_KEY, resourceKey.getString())
|
||||||
.param(PARAM_REQUESTING_CLIENT_ID, requestingClientId)
|
.param(PARAM_REQUESTING_CLIENT_ID, requestingClientId)
|
||||||
|
.param(PARAM_CLIENT_TRANSACTION_ID, trid.getClientTransactionId())
|
||||||
|
.param(PARAM_SERVER_TRANSACTION_ID, trid.getServerTransactionId())
|
||||||
.param(PARAM_IS_SUPERUSER, Boolean.toString(isSuperuser));
|
.param(PARAM_IS_SUPERUSER, Boolean.toString(isSuperuser));
|
||||||
addTaskToQueueWithRetry(asyncDeletePullQueue, task);
|
addTaskToQueueWithRetry(asyncDeletePullQueue, task);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.eppcommon.AuthInfo;
|
import google.registry.model.eppcommon.AuthInfo;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.eppoutput.EppResponse;
|
import google.registry.model.eppoutput.EppResponse;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||||
|
@ -78,6 +79,7 @@ public final class ContactDeleteFlow implements TransactionalFlow {
|
||||||
@Inject ExtensionManager extensionManager;
|
@Inject ExtensionManager extensionManager;
|
||||||
@Inject @ClientId String clientId;
|
@Inject @ClientId String clientId;
|
||||||
@Inject @TargetId String targetId;
|
@Inject @TargetId String targetId;
|
||||||
|
@Inject Trid trid;
|
||||||
@Inject @Superuser boolean isSuperuser;
|
@Inject @Superuser boolean isSuperuser;
|
||||||
@Inject Optional<AuthInfo> authInfo;
|
@Inject Optional<AuthInfo> authInfo;
|
||||||
@Inject HistoryEntry.Builder historyBuilder;
|
@Inject HistoryEntry.Builder historyBuilder;
|
||||||
|
@ -98,7 +100,7 @@ public final class ContactDeleteFlow implements TransactionalFlow {
|
||||||
if (!isSuperuser) {
|
if (!isSuperuser) {
|
||||||
verifyResourceOwnership(clientId, existingContact);
|
verifyResourceOwnership(clientId, existingContact);
|
||||||
}
|
}
|
||||||
asyncFlowEnqueuer.enqueueAsyncDelete(existingContact, clientId, isSuperuser);
|
asyncFlowEnqueuer.enqueueAsyncDelete(existingContact, clientId, trid, isSuperuser);
|
||||||
ContactResource newContact =
|
ContactResource newContact =
|
||||||
existingContact.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build();
|
existingContact.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build();
|
||||||
historyBuilder
|
historyBuilder
|
||||||
|
|
|
@ -38,6 +38,7 @@ import google.registry.model.EppResource;
|
||||||
import google.registry.model.domain.DomainBase;
|
import google.registry.model.domain.DomainBase;
|
||||||
import google.registry.model.domain.metadata.MetadataExtension;
|
import google.registry.model.domain.metadata.MetadataExtension;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.eppoutput.EppResponse;
|
import google.registry.model.eppoutput.EppResponse;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
@ -80,6 +81,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||||
@Inject ExtensionManager extensionManager;
|
@Inject ExtensionManager extensionManager;
|
||||||
@Inject @ClientId String clientId;
|
@Inject @ClientId String clientId;
|
||||||
@Inject @TargetId String targetId;
|
@Inject @TargetId String targetId;
|
||||||
|
@Inject Trid trid;
|
||||||
@Inject @Superuser boolean isSuperuser;
|
@Inject @Superuser boolean isSuperuser;
|
||||||
@Inject HistoryEntry.Builder historyBuilder;
|
@Inject HistoryEntry.Builder historyBuilder;
|
||||||
@Inject AsyncFlowEnqueuer asyncFlowEnqueuer;
|
@Inject AsyncFlowEnqueuer asyncFlowEnqueuer;
|
||||||
|
@ -106,7 +108,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||||
: existingHost;
|
: existingHost;
|
||||||
verifyResourceOwnership(clientId, owningResource);
|
verifyResourceOwnership(clientId, owningResource);
|
||||||
}
|
}
|
||||||
asyncFlowEnqueuer.enqueueAsyncDelete(existingHost, clientId, isSuperuser);
|
asyncFlowEnqueuer.enqueueAsyncDelete(existingHost, clientId, trid, isSuperuser);
|
||||||
HostResource newHost =
|
HostResource newHost =
|
||||||
existingHost.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build();
|
existingHost.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build();
|
||||||
historyBuilder
|
historyBuilder
|
||||||
|
|
|
@ -60,6 +60,7 @@ import google.registry.model.host.HostInfoData;
|
||||||
import google.registry.model.poll.MessageQueueInfo;
|
import google.registry.model.poll.MessageQueueInfo;
|
||||||
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
||||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||||
|
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
|
||||||
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 javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -111,7 +112,9 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting {
|
||||||
@XmlElementRef(type = DomainTransferResponse.class),
|
@XmlElementRef(type = DomainTransferResponse.class),
|
||||||
@XmlElementRef(type = HostCheckData.class),
|
@XmlElementRef(type = HostCheckData.class),
|
||||||
@XmlElementRef(type = HostCreateData.class),
|
@XmlElementRef(type = HostCreateData.class),
|
||||||
@XmlElementRef(type = HostInfoData.class)})
|
@XmlElementRef(type = HostInfoData.class),
|
||||||
|
@XmlElementRef(type = HostPendingActionNotificationResponse.class)
|
||||||
|
})
|
||||||
@XmlElementWrapper
|
@XmlElementWrapper
|
||||||
ImmutableList<? extends ResponseData> resData;
|
ImmutableList<? extends ResponseData> resData;
|
||||||
|
|
||||||
|
|
|
@ -125,4 +125,30 @@ public abstract class PendingActionNotificationResponse
|
||||||
processedDate);
|
processedDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** An adapter to output the XML in response to resolving a pending command on a host. */
|
||||||
|
@Embed
|
||||||
|
@XmlRootElement(name = "panData", namespace = "urn:ietf:params:xml:ns:domain-1.0")
|
||||||
|
@XmlType(
|
||||||
|
propOrder = {"name", "trid", "processedDate"},
|
||||||
|
namespace = "urn:ietf:params:xml:ns:domain-1.0"
|
||||||
|
)
|
||||||
|
public static class HostPendingActionNotificationResponse
|
||||||
|
extends PendingActionNotificationResponse {
|
||||||
|
|
||||||
|
@XmlElement
|
||||||
|
NameOrId getName() {
|
||||||
|
return nameOrId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HostPendingActionNotificationResponse create(
|
||||||
|
String fullyQualifiedHostName, boolean actionResult, Trid trid, DateTime processedDate) {
|
||||||
|
return init(
|
||||||
|
new HostPendingActionNotificationResponse(),
|
||||||
|
fullyQualifiedHostName,
|
||||||
|
actionResult,
|
||||||
|
trid,
|
||||||
|
processedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import google.registry.model.eppoutput.EppResponse.ResponseData;
|
||||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||||
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
||||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||||
|
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||||
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
|
||||||
|
@ -189,6 +190,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||||
List<ContactTransferResponse> contactTransferResponses;
|
List<ContactTransferResponse> contactTransferResponses;
|
||||||
List<DomainPendingActionNotificationResponse> domainPendingActionNotificationResponses;
|
List<DomainPendingActionNotificationResponse> domainPendingActionNotificationResponses;
|
||||||
List<DomainTransferResponse> domainTransferResponses;
|
List<DomainTransferResponse> domainTransferResponses;
|
||||||
|
List<HostPendingActionNotificationResponse> hostPendingActionNotificationResponses;
|
||||||
|
|
||||||
// Extensions. Objectify cannot persist a base class type, so we must have a separate field
|
// Extensions. Objectify cannot persist a base class type, so we must have a separate field
|
||||||
// to hold every possible derived type of ResponseExtensions that we might store.
|
// to hold every possible derived type of ResponseExtensions that we might store.
|
||||||
|
@ -211,6 +213,7 @@ public abstract class PollMessage extends ImmutableObject
|
||||||
.addAll(nullToEmpty(contactTransferResponses))
|
.addAll(nullToEmpty(contactTransferResponses))
|
||||||
.addAll(nullToEmpty(domainPendingActionNotificationResponses))
|
.addAll(nullToEmpty(domainPendingActionNotificationResponses))
|
||||||
.addAll(nullToEmpty(domainTransferResponses))
|
.addAll(nullToEmpty(domainTransferResponses))
|
||||||
|
.addAll(nullToEmpty(hostPendingActionNotificationResponses))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +243,8 @@ public abstract class PollMessage extends ImmutableObject
|
||||||
iterable.filter(DomainPendingActionNotificationResponse.class).toList());
|
iterable.filter(DomainPendingActionNotificationResponse.class).toList());
|
||||||
getInstance().domainTransferResponses = forceEmptyToNull(
|
getInstance().domainTransferResponses = forceEmptyToNull(
|
||||||
iterable.filter(DomainTransferResponse.class).toList());
|
iterable.filter(DomainTransferResponse.class).toList());
|
||||||
|
getInstance().hostPendingActionNotificationResponses = forceEmptyToNull(
|
||||||
|
iterable.filter(HostPendingActionNotificationResponse.class).toList());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,9 +69,12 @@ import google.registry.model.contact.PostalInfo;
|
||||||
import google.registry.model.domain.DomainResource;
|
import google.registry.model.domain.DomainResource;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
import google.registry.model.eppcommon.Trid;
|
import google.registry.model.eppcommon.Trid;
|
||||||
|
import google.registry.model.eppoutput.EppResponse.ResponseData;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.ofy.Ofy;
|
import google.registry.model.ofy.Ofy;
|
||||||
import google.registry.model.poll.PendingActionNotificationResponse;
|
import google.registry.model.poll.PendingActionNotificationResponse;
|
||||||
|
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
|
||||||
|
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
|
||||||
import google.registry.model.poll.PollMessage;
|
import google.registry.model.poll.PollMessage;
|
||||||
import google.registry.model.poll.PollMessage.OneTime;
|
import google.registry.model.poll.PollMessage.OneTime;
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
|
@ -147,7 +150,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
public void testSuccess_contact_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
|
public void testSuccess_contact_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
|
||||||
ContactResource contact = persistContactPendingDelete("blah8221");
|
ContactResource contact = persistContactPendingDelete("blah8221");
|
||||||
persistResource(newDomainResource("example.tld", contact));
|
persistResource(newDomainResource("example.tld", contact));
|
||||||
enqueuer.enqueueAsyncDelete(contact, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contact, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
ContactResource contactUpdated =
|
ContactResource contactUpdated =
|
||||||
loadByForeignKey(ContactResource.class, "blah8221", clock.nowUtc());
|
loadByForeignKey(ContactResource.class, "blah8221", clock.nowUtc());
|
||||||
|
@ -164,14 +168,17 @@ public class DeleteContactsAndHostsActionTest
|
||||||
assertPollMessageFor(
|
assertPollMessageFor(
|
||||||
historyEntry,
|
historyEntry,
|
||||||
"TheRegistrar",
|
"TheRegistrar",
|
||||||
"Can't delete contact blah8221 because it is referenced by a domain.");
|
"Can't delete contact blah8221 because it is referenced by a domain.",
|
||||||
|
false,
|
||||||
|
contact);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_contact_notReferenced_getsDeleted_andPiiWipedOut() throws Exception {
|
public void testSuccess_contact_notReferenced_getsDeleted_andPiiWipedOut() throws Exception {
|
||||||
ContactResource contact = persistContactWithPii("jim919");
|
ContactResource contact = persistContactWithPii("jim919");
|
||||||
enqueuer.enqueueAsyncDelete(contact, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contact, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "jim919", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(ContactResource.class, "jim919", clock.nowUtc())).isNull();
|
||||||
ContactResource contactAfterDeletion = ofy().load().entity(contact).now();
|
ContactResource contactAfterDeletion = ofy().load().entity(contact).now();
|
||||||
|
@ -195,7 +202,7 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.and()
|
.and()
|
||||||
.hasNullFaxNumber();
|
.hasNullFaxNumber();
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(contactAfterDeletion, CONTACT_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(contactAfterDeletion, CONTACT_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact jim919.");
|
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact jim919.", true, contact);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +210,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
public void testSuccess_contactWithoutPendingTransfer_isDeletedAndHasNoTransferData()
|
public void testSuccess_contactWithoutPendingTransfer_isDeletedAndHasNoTransferData()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
ContactResource contact = persistContactPendingDelete("blah8221");
|
ContactResource contact = persistContactPendingDelete("blah8221");
|
||||||
enqueuer.enqueueAsyncDelete(contact, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contact, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
ContactResource contactAfterDeletion = ofy().load().entity(contact).now();
|
ContactResource contactAfterDeletion = ofy().load().entity(contact).now();
|
||||||
assertThat(contactAfterDeletion.getTransferData()).isEqualTo(TransferData.EMPTY);
|
assertThat(contactAfterDeletion.getTransferData()).isEqualTo(TransferData.EMPTY);
|
||||||
|
@ -218,7 +226,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
transferRequestTime,
|
transferRequestTime,
|
||||||
transferRequestTime.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD),
|
transferRequestTime.plus(Registry.DEFAULT_TRANSFER_GRACE_PERIOD),
|
||||||
clock.nowUtc());
|
clock.nowUtc());
|
||||||
enqueuer.enqueueAsyncDelete(contact, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contact, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
// Check that the contact is deleted as of now.
|
// Check that the contact is deleted as of now.
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())).isNull();
|
||||||
|
@ -263,7 +272,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.setDeletionTime(clock.nowUtc().minusDays(3))
|
.setDeletionTime(clock.nowUtc().minusDays(3))
|
||||||
.build());
|
.build());
|
||||||
enqueuer.enqueueAsyncDelete(contactUsed, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contactUsed, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "blah1234", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(ContactResource.class, "blah1234", clock.nowUtc())).isNull();
|
||||||
ContactResource contactBeforeDeletion =
|
ContactResource contactBeforeDeletion =
|
||||||
|
@ -279,14 +289,16 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(CONTACT_DELETE);
|
.hasType(CONTACT_DELETE);
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(contactBeforeDeletion, CONTACT_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(contactBeforeDeletion, CONTACT_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted contact blah1234.");
|
assertPollMessageFor(
|
||||||
|
historyEntry, "TheRegistrar", "Deleted contact blah1234.", true, contactUsed);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_contact_notRequestedByOwner_doesNotGetDeleted() throws Exception {
|
public void testSuccess_contact_notRequestedByOwner_doesNotGetDeleted() throws Exception {
|
||||||
ContactResource contact = persistContactPendingDelete("jane0991");
|
ContactResource contact = persistContactPendingDelete("jane0991");
|
||||||
enqueuer.enqueueAsyncDelete(contact, "OtherRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contact, "OtherRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
ContactResource contactAfter =
|
ContactResource contactAfter =
|
||||||
loadByForeignKey(ContactResource.class, "jane0991", clock.nowUtc());
|
loadByForeignKey(ContactResource.class, "jane0991", clock.nowUtc());
|
||||||
|
@ -299,14 +311,17 @@ public class DeleteContactsAndHostsActionTest
|
||||||
assertPollMessageFor(
|
assertPollMessageFor(
|
||||||
historyEntry,
|
historyEntry,
|
||||||
"OtherRegistrar",
|
"OtherRegistrar",
|
||||||
"Can't delete contact jane0991 because it was transferred prior to deletion.");
|
"Can't delete contact jane0991 because it was transferred prior to deletion.",
|
||||||
|
false,
|
||||||
|
contact);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_contact_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
|
public void testSuccess_contact_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
|
||||||
ContactResource contact = persistContactWithPii("nate007");
|
ContactResource contact = persistContactWithPii("nate007");
|
||||||
enqueuer.enqueueAsyncDelete(contact, "OtherRegistrar", true);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
contact, "OtherRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), true);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "nate007", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(ContactResource.class, "nate007", clock.nowUtc())).isNull();
|
||||||
ContactResource contactAfterDeletion = ofy().load().entity(contact).now();
|
ContactResource contactAfterDeletion = ofy().load().entity(contact).now();
|
||||||
|
@ -330,7 +345,7 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.and()
|
.and()
|
||||||
.hasNullFaxNumber();
|
.hasNullFaxNumber();
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(contactAfterDeletion, CONTACT_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(contactAfterDeletion, CONTACT_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted contact nate007.");
|
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted contact nate007.", true, contact);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,10 +353,14 @@ public class DeleteContactsAndHostsActionTest
|
||||||
public void testSuccess_targetResourcesDontExist_areDelayedForADay() throws Exception {
|
public void testSuccess_targetResourcesDontExist_areDelayedForADay() throws Exception {
|
||||||
ContactResource contactNotSaved = newContactResource("somecontact");
|
ContactResource contactNotSaved = newContactResource("somecontact");
|
||||||
HostResource hostNotSaved = newHostResource("a11.blah.foo");
|
HostResource hostNotSaved = newHostResource("a11.blah.foo");
|
||||||
enqueuer.enqueueAsyncDelete(contactNotSaved, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
enqueuer.enqueueAsyncDelete(hostNotSaved, "TheRegistrar", false);
|
contactNotSaved, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
hostNotSaved, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
String payloadFormat = "resourceKey=%s&requestingClientId=TheRegistrar&isSuperuser=false";
|
String payloadFormat =
|
||||||
|
"resourceKey=%s&requestingClientId=TheRegistrar&"
|
||||||
|
+ "clientTransactionId=fakeClientTrid&serverTransactionId=fakeServerTrid&isSuperuser=false";
|
||||||
assertTasksEnqueued(
|
assertTasksEnqueued(
|
||||||
QUEUE_ASYNC_DELETE,
|
QUEUE_ASYNC_DELETE,
|
||||||
new TaskMatcher()
|
new TaskMatcher()
|
||||||
|
@ -369,8 +388,10 @@ public class DeleteContactsAndHostsActionTest
|
||||||
public void testSuccess_resourcesNotInPendingDelete_areSkipped() throws Exception {
|
public void testSuccess_resourcesNotInPendingDelete_areSkipped() throws Exception {
|
||||||
ContactResource contact = persistActiveContact("blah2222");
|
ContactResource contact = persistActiveContact("blah2222");
|
||||||
HostResource host = persistActiveHost("rustles.your.jimmies");
|
HostResource host = persistActiveHost("rustles.your.jimmies");
|
||||||
enqueuer.enqueueAsyncDelete(contact, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
enqueuer.enqueueAsyncDelete(host, "TheRegistrar", false);
|
contact, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(ContactResource.class, "blah2222", clock.nowUtc()))
|
assertThat(loadByForeignKey(ContactResource.class, "blah2222", clock.nowUtc()))
|
||||||
.isEqualTo(contact);
|
.isEqualTo(contact);
|
||||||
|
@ -383,8 +404,10 @@ public class DeleteContactsAndHostsActionTest
|
||||||
public void testSuccess_alreadyDeletedResources_areSkipped() throws Exception {
|
public void testSuccess_alreadyDeletedResources_areSkipped() throws Exception {
|
||||||
ContactResource contactDeleted = persistDeletedContact("blah1236", clock.nowUtc().minusDays(2));
|
ContactResource contactDeleted = persistDeletedContact("blah1236", clock.nowUtc().minusDays(2));
|
||||||
HostResource hostDeleted = persistDeletedHost("a.lim.lop", clock.nowUtc().minusDays(3));
|
HostResource hostDeleted = persistDeletedHost("a.lim.lop", clock.nowUtc().minusDays(3));
|
||||||
enqueuer.enqueueAsyncDelete(contactDeleted, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
enqueuer.enqueueAsyncDelete(hostDeleted, "TheRegistrar", false);
|
contactDeleted, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
hostDeleted, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(ofy().load().entity(contactDeleted).now()).isEqualTo(contactDeleted);
|
assertThat(ofy().load().entity(contactDeleted).now()).isEqualTo(contactDeleted);
|
||||||
assertThat(ofy().load().entity(hostDeleted).now()).isEqualTo(hostDeleted);
|
assertThat(ofy().load().entity(hostDeleted).now()).isEqualTo(hostDeleted);
|
||||||
|
@ -395,7 +418,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
public void testSuccess_host_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
|
public void testSuccess_host_referencedByActiveDomain_doesNotGetDeleted() throws Exception {
|
||||||
HostResource host = persistHostPendingDelete("ns1.example.tld");
|
HostResource host = persistHostPendingDelete("ns1.example.tld");
|
||||||
persistUsedDomain("example.tld", persistActiveContact("abc456"), host);
|
persistUsedDomain("example.tld", persistActiveContact("abc456"), host);
|
||||||
enqueuer.enqueueAsyncDelete(host, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
HostResource hostAfter =
|
HostResource hostAfter =
|
||||||
loadByForeignKey(HostResource.class, "ns1.example.tld", clock.nowUtc());
|
loadByForeignKey(HostResource.class, "ns1.example.tld", clock.nowUtc());
|
||||||
|
@ -410,14 +434,17 @@ public class DeleteContactsAndHostsActionTest
|
||||||
assertPollMessageFor(
|
assertPollMessageFor(
|
||||||
historyEntry,
|
historyEntry,
|
||||||
"TheRegistrar",
|
"TheRegistrar",
|
||||||
"Can't delete host ns1.example.tld because it is referenced by a domain.");
|
"Can't delete host ns1.example.tld because it is referenced by a domain.",
|
||||||
|
false,
|
||||||
|
host);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_host_notReferenced_getsDeleted() throws Exception {
|
public void testSuccess_host_notReferenced_getsDeleted() throws Exception {
|
||||||
HostResource host = persistHostPendingDelete("ns2.example.tld");
|
HostResource host = persistHostPendingDelete("ns2.example.tld");
|
||||||
enqueuer.enqueueAsyncDelete(host, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc())).isNull();
|
||||||
HostResource hostBeforeDeletion =
|
HostResource hostBeforeDeletion =
|
||||||
|
@ -433,7 +460,7 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HOST_DELETE);
|
.hasType(HOST_DELETE);
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
|
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.", true, host);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +473,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.setNameservers(ImmutableSet.of(Key.create(host)))
|
.setNameservers(ImmutableSet.of(Key.create(host)))
|
||||||
.setDeletionTime(clock.nowUtc().minusDays(5))
|
.setDeletionTime(clock.nowUtc().minusDays(5))
|
||||||
.build());
|
.build());
|
||||||
enqueuer.enqueueAsyncDelete(host, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns1.example.tld", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(HostResource.class, "ns1.example.tld", clock.nowUtc())).isNull();
|
||||||
HostResource hostBeforeDeletion =
|
HostResource hostBeforeDeletion =
|
||||||
|
@ -462,7 +490,7 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HOST_DELETE);
|
.hasType(HOST_DELETE);
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns1.example.tld.");
|
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns1.example.tld.", true, host);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,7 +508,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.setSuperordinateDomain(Key.create(domain))
|
.setSuperordinateDomain(Key.create(domain))
|
||||||
.build());
|
.build());
|
||||||
enqueuer.enqueueAsyncDelete(host, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
// Check that the host is deleted as of now.
|
// Check that the host is deleted as of now.
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc())).isNull();
|
||||||
|
@ -501,14 +530,15 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HOST_DELETE);
|
.hasType(HOST_DELETE);
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.");
|
assertPollMessageFor(historyEntry, "TheRegistrar", "Deleted host ns2.example.tld.", true, host);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_host_notRequestedByOwner_doesNotGetDeleted() throws Exception {
|
public void testSuccess_host_notRequestedByOwner_doesNotGetDeleted() throws Exception {
|
||||||
HostResource host = persistHostPendingDelete("ns2.example.tld");
|
HostResource host = persistHostPendingDelete("ns2.example.tld");
|
||||||
enqueuer.enqueueAsyncDelete(host, "OtherRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "OtherRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
HostResource hostAfter =
|
HostResource hostAfter =
|
||||||
loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc());
|
loadByForeignKey(HostResource.class, "ns2.example.tld", clock.nowUtc());
|
||||||
|
@ -521,14 +551,17 @@ public class DeleteContactsAndHostsActionTest
|
||||||
assertPollMessageFor(
|
assertPollMessageFor(
|
||||||
historyEntry,
|
historyEntry,
|
||||||
"OtherRegistrar",
|
"OtherRegistrar",
|
||||||
"Can't delete host ns2.example.tld because it was transferred prior to deletion.");
|
"Can't delete host ns2.example.tld because it was transferred prior to deletion.",
|
||||||
|
false,
|
||||||
|
host);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_host_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
|
public void testSuccess_host_notRequestedByOwner_isSuperuser_getsDeleted() throws Exception {
|
||||||
HostResource host = persistHostPendingDelete("ns66.example.tld");
|
HostResource host = persistHostPendingDelete("ns66.example.tld");
|
||||||
enqueuer.enqueueAsyncDelete(host, "OtherRegistrar", true);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
host, "OtherRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), true);
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns66.example.tld", clock.nowUtc())).isNull();
|
assertThat(loadByForeignKey(HostResource.class, "ns66.example.tld", clock.nowUtc())).isNull();
|
||||||
HostResource hostBeforeDeletion =
|
HostResource hostBeforeDeletion =
|
||||||
|
@ -544,7 +577,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HOST_DELETE);
|
.hasType(HOST_DELETE);
|
||||||
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(hostBeforeDeletion, HOST_DELETE);
|
||||||
assertPollMessageFor(historyEntry, "OtherRegistrar", "Deleted host ns66.example.tld.");
|
assertPollMessageFor(
|
||||||
|
historyEntry, "OtherRegistrar", "Deleted host ns66.example.tld.", true, host);
|
||||||
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
assertNoTasksEnqueued(QUEUE_ASYNC_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,7 +594,8 @@ public class DeleteContactsAndHostsActionTest
|
||||||
HostResource h4 = persistHostPendingDelete("used.host.com");
|
HostResource h4 = persistHostPendingDelete("used.host.com");
|
||||||
persistUsedDomain("usescontactandhost.tld", c4, h4);
|
persistUsedDomain("usescontactandhost.tld", c4, h4);
|
||||||
for (EppResource resource : ImmutableList.<EppResource>of(c1, c2, c3, c4, h1, h2, h3, h4)) {
|
for (EppResource resource : ImmutableList.<EppResource>of(c1, c2, c3, c4, h1, h2, h3, h4)) {
|
||||||
enqueuer.enqueueAsyncDelete(resource, "TheRegistrar", false);
|
enqueuer.enqueueAsyncDelete(
|
||||||
|
resource, "TheRegistrar", Trid.create("fakeClientTrid", "fakeServerTrid"), false);
|
||||||
}
|
}
|
||||||
runMapreduce();
|
runMapreduce();
|
||||||
for (EppResource resource : ImmutableList.<EppResource>of(c1, c2, c3, h1, h2, h3)) {
|
for (EppResource resource : ImmutableList.<EppResource>of(c1, c2, c3, h1, h2, h3)) {
|
||||||
|
@ -605,13 +640,38 @@ public class DeleteContactsAndHostsActionTest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to check that one poll message exists with a given history entry, resource,
|
* Helper method to check that one poll message exists with a given history entry, resource,
|
||||||
* client id, and message.
|
* client id, and message. Also checks that the only resulting async response matches the resource
|
||||||
|
* type, and has the appropriate actionResult, nameOrId, and Trid.
|
||||||
*/
|
*/
|
||||||
private static void assertPollMessageFor(HistoryEntry historyEntry, String clientId, String msg) {
|
private static void assertPollMessageFor(
|
||||||
|
HistoryEntry historyEntry,
|
||||||
|
String clientId,
|
||||||
|
String msg,
|
||||||
|
boolean expectedActionResult,
|
||||||
|
EppResource resource) {
|
||||||
PollMessage.OneTime pollMessage = (OneTime) getOnlyPollMessageForHistoryEntry(historyEntry);
|
PollMessage.OneTime pollMessage = (OneTime) getOnlyPollMessageForHistoryEntry(historyEntry);
|
||||||
assertThat(msg).isEqualTo(pollMessage.getMsg());
|
assertThat(pollMessage.getMsg()).isEqualTo(msg);
|
||||||
assertThat(clientId).isEqualTo(pollMessage.getClientId());
|
|
||||||
assertThat(pollMessage.getClientId()).isEqualTo(clientId);
|
assertThat(pollMessage.getClientId()).isEqualTo(clientId);
|
||||||
|
|
||||||
|
ImmutableList<ResponseData> pollResponses = pollMessage.getResponseData();
|
||||||
|
assertThat(pollResponses).hasSize(1);
|
||||||
|
ResponseData responseData = pollMessage.getResponseData().get(0);
|
||||||
|
|
||||||
|
String expectedResourceName;
|
||||||
|
if (resource instanceof HostResource) {
|
||||||
|
assertThat(responseData).isInstanceOf(HostPendingActionNotificationResponse.class);
|
||||||
|
expectedResourceName = ((HostResource) resource).getFullyQualifiedHostName();
|
||||||
|
} else {
|
||||||
|
assertThat(responseData).isInstanceOf(ContactPendingActionNotificationResponse.class);
|
||||||
|
expectedResourceName = ((ContactResource) resource).getContactId();
|
||||||
|
}
|
||||||
|
PendingActionNotificationResponse pendingResponse =
|
||||||
|
(PendingActionNotificationResponse) responseData;
|
||||||
|
assertThat(pendingResponse.getActionResult()).isEqualTo(expectedActionResult);
|
||||||
|
assertThat(pendingResponse.getNameAsString()).isEqualTo(expectedResourceName);
|
||||||
|
Trid trid = pendingResponse.getTrid();
|
||||||
|
assertThat(trid.getClientTransactionId()).isEqualTo("fakeClientTrid");
|
||||||
|
assertThat(trid.getServerTransactionId()).isEqualTo("fakeServerTrid");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ContactResource persistContactPendingDelete(String contactId) {
|
private static ContactResource persistContactPendingDelete(String contactId) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import google.registry.model.EppResource;
|
||||||
import google.registry.model.EppResourceUtils;
|
import google.registry.model.EppResourceUtils;
|
||||||
import google.registry.model.domain.DomainApplication;
|
import google.registry.model.domain.DomainApplication;
|
||||||
import google.registry.model.domain.launch.ApplicationIdTargetExtension;
|
import google.registry.model.domain.launch.ApplicationIdTargetExtension;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
||||||
import google.registry.model.eppinput.ResourceCommand;
|
import google.registry.model.eppinput.ResourceCommand;
|
||||||
import google.registry.model.index.EppResourceIndex;
|
import google.registry.model.index.EppResourceIndex;
|
||||||
|
@ -155,11 +156,16 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||||
|
|
||||||
/** Asserts the presence of a single enqueued async contact or host deletion */
|
/** Asserts the presence of a single enqueued async contact or host deletion */
|
||||||
protected static <T extends EppResource> void assertAsyncDeletionTaskEnqueued(
|
protected static <T extends EppResource> void assertAsyncDeletionTaskEnqueued(
|
||||||
T resource, String requestingClientId, boolean isSuperuser) throws Exception {
|
T resource, String requestingClientId, Trid trid, boolean isSuperuser) throws Exception {
|
||||||
String expectedPayload =
|
String expectedPayload =
|
||||||
String.format(
|
String.format(
|
||||||
"resourceKey=%s&requestingClientId=%s&isSuperuser=%s",
|
"resourceKey=%s&requestingClientId=%s&clientTransactionId=%s&"
|
||||||
Key.create(resource).getString(), requestingClientId, Boolean.toString(isSuperuser));
|
+ "serverTransactionId=%s&isSuperuser=%s",
|
||||||
|
Key.create(resource).getString(),
|
||||||
|
requestingClientId,
|
||||||
|
trid.getClientTransactionId(),
|
||||||
|
trid.getServerTransactionId(),
|
||||||
|
Boolean.toString(isSuperuser));
|
||||||
assertTasksEnqueued(
|
assertTasksEnqueued(
|
||||||
"async-delete-pull",
|
"async-delete-pull",
|
||||||
new TaskMatcher()
|
new TaskMatcher()
|
||||||
|
|
|
@ -31,6 +31,7 @@ import google.registry.flows.exceptions.ResourceStatusProhibitsOperationExceptio
|
||||||
import google.registry.flows.exceptions.ResourceToDeleteIsReferencedException;
|
import google.registry.flows.exceptions.ResourceToDeleteIsReferencedException;
|
||||||
import google.registry.model.contact.ContactResource;
|
import google.registry.model.contact.ContactResource;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -58,7 +59,8 @@ public class ContactDeleteFlowTest
|
||||||
runFlowAssertResponse(readFile("contact_delete_response.xml"));
|
runFlowAssertResponse(readFile("contact_delete_response.xml"));
|
||||||
ContactResource deletedContact = reloadResourceByForeignKey();
|
ContactResource deletedContact = reloadResourceByForeignKey();
|
||||||
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
|
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
|
||||||
assertAsyncDeletionTaskEnqueued(deletedContact, "TheRegistrar", false);
|
assertAsyncDeletionTaskEnqueued(
|
||||||
|
deletedContact, "TheRegistrar", Trid.create("ABC-12345", "server-trid"), false);
|
||||||
assertAboutContacts().that(deletedContact)
|
assertAboutContacts().that(deletedContact)
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
|
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
|
||||||
|
@ -127,7 +129,8 @@ public class ContactDeleteFlowTest
|
||||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_delete_response.xml"));
|
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("contact_delete_response.xml"));
|
||||||
ContactResource deletedContact = reloadResourceByForeignKey();
|
ContactResource deletedContact = reloadResourceByForeignKey();
|
||||||
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
|
assertAboutContacts().that(deletedContact).hasStatusValue(StatusValue.PENDING_DELETE);
|
||||||
assertAsyncDeletionTaskEnqueued(deletedContact, "NewRegistrar", true);
|
assertAsyncDeletionTaskEnqueued(
|
||||||
|
deletedContact, "NewRegistrar", Trid.create("ABC-12345", "server-trid"), true);
|
||||||
assertAboutContacts().that(deletedContact)
|
assertAboutContacts().that(deletedContact)
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
|
.hasType(HistoryEntry.Type.CONTACT_PENDING_DELETE);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
|
||||||
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
|
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
|
||||||
import google.registry.model.domain.DomainResource;
|
import google.registry.model.domain.DomainResource;
|
||||||
import google.registry.model.eppcommon.StatusValue;
|
import google.registry.model.eppcommon.StatusValue;
|
||||||
|
import google.registry.model.eppcommon.Trid;
|
||||||
import google.registry.model.host.HostResource;
|
import google.registry.model.host.HostResource;
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.model.reporting.HistoryEntry;
|
import google.registry.model.reporting.HistoryEntry;
|
||||||
|
@ -68,7 +69,8 @@ public class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, Hos
|
||||||
runFlowAssertResponse(readFile("host_delete_response.xml"));
|
runFlowAssertResponse(readFile("host_delete_response.xml"));
|
||||||
HostResource deletedHost = reloadResourceByForeignKey();
|
HostResource deletedHost = reloadResourceByForeignKey();
|
||||||
assertAboutHosts().that(deletedHost).hasStatusValue(StatusValue.PENDING_DELETE);
|
assertAboutHosts().that(deletedHost).hasStatusValue(StatusValue.PENDING_DELETE);
|
||||||
assertAsyncDeletionTaskEnqueued(deletedHost, "TheRegistrar", false);
|
assertAsyncDeletionTaskEnqueued(
|
||||||
|
deletedHost, "TheRegistrar", Trid.create("ABC-12345", "server-trid"), false);
|
||||||
assertAboutHosts().that(deletedHost)
|
assertAboutHosts().that(deletedHost)
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HistoryEntry.Type.HOST_PENDING_DELETE);
|
.hasType(HistoryEntry.Type.HOST_PENDING_DELETE);
|
||||||
|
@ -134,7 +136,8 @@ public class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, Hos
|
||||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("host_delete_response.xml"));
|
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("host_delete_response.xml"));
|
||||||
HostResource deletedHost = reloadResourceByForeignKey();
|
HostResource deletedHost = reloadResourceByForeignKey();
|
||||||
assertAboutHosts().that(deletedHost).hasStatusValue(StatusValue.PENDING_DELETE);
|
assertAboutHosts().that(deletedHost).hasStatusValue(StatusValue.PENDING_DELETE);
|
||||||
assertAsyncDeletionTaskEnqueued(deletedHost, "NewRegistrar", true);
|
assertAsyncDeletionTaskEnqueued(
|
||||||
|
deletedHost, "NewRegistrar", Trid.create("ABC-12345", "server-trid"), true);
|
||||||
assertAboutHosts().that(deletedHost)
|
assertAboutHosts().that(deletedHost)
|
||||||
.hasOnlyOneHistoryEntryWhich()
|
.hasOnlyOneHistoryEntryWhich()
|
||||||
.hasType(HistoryEntry.Type.HOST_PENDING_DELETE);
|
.hasType(HistoryEntry.Type.HOST_PENDING_DELETE);
|
||||||
|
|
|
@ -550,6 +550,11 @@ class google.registry.model.poll.PendingActionNotificationResponse$DomainPending
|
||||||
google.registry.model.poll.PendingActionNotificationResponse$NameOrId nameOrId;
|
google.registry.model.poll.PendingActionNotificationResponse$NameOrId nameOrId;
|
||||||
org.joda.time.DateTime processedDate;
|
org.joda.time.DateTime processedDate;
|
||||||
}
|
}
|
||||||
|
class google.registry.model.poll.PendingActionNotificationResponse$HostPendingActionNotificationResponse {
|
||||||
|
google.registry.model.eppcommon.Trid trid;
|
||||||
|
google.registry.model.poll.PendingActionNotificationResponse$NameOrId nameOrId;
|
||||||
|
org.joda.time.DateTime processedDate;
|
||||||
|
}
|
||||||
class google.registry.model.poll.PendingActionNotificationResponse$NameOrId {
|
class google.registry.model.poll.PendingActionNotificationResponse$NameOrId {
|
||||||
boolean actionResult;
|
boolean actionResult;
|
||||||
java.lang.String value;
|
java.lang.String value;
|
||||||
|
@ -578,6 +583,7 @@ class google.registry.model.poll.PollMessage$OneTime {
|
||||||
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;
|
||||||
java.util.List<google.registry.model.poll.PendingActionNotificationResponse$DomainPendingActionNotificationResponse> domainPendingActionNotificationResponses;
|
java.util.List<google.registry.model.poll.PendingActionNotificationResponse$DomainPendingActionNotificationResponse> domainPendingActionNotificationResponses;
|
||||||
|
java.util.List<google.registry.model.poll.PendingActionNotificationResponse$HostPendingActionNotificationResponse> hostPendingActionNotificationResponses;
|
||||||
java.util.List<google.registry.model.transfer.TransferResponse$ContactTransferResponse> contactTransferResponses;
|
java.util.List<google.registry.model.transfer.TransferResponse$ContactTransferResponse> contactTransferResponses;
|
||||||
java.util.List<google.registry.model.transfer.TransferResponse$DomainTransferResponse> domainTransferResponses;
|
java.util.List<google.registry.model.transfer.TransferResponse$DomainTransferResponse> domainTransferResponses;
|
||||||
org.joda.time.DateTime eventTime;
|
org.joda.time.DateTime eventTime;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue