diff --git a/java/google/registry/tools/server/ResaveAllEppResourcesAction.java b/java/google/registry/tools/server/ResaveAllEppResourcesAction.java index e30dc3bdf..001dfe332 100644 --- a/java/google/registry/tools/server/ResaveAllEppResourcesAction.java +++ b/java/google/registry/tools/server/ResaveAllEppResourcesAction.java @@ -16,6 +16,7 @@ package google.registry.tools.server; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.PipelineUtils.createJobPath; +import static org.joda.time.DateTimeZone.UTC; import com.google.appengine.tools.mapreduce.Mapper; import com.google.common.collect.ImmutableList; @@ -28,13 +29,15 @@ import google.registry.request.Action; import google.registry.request.Response; import google.registry.request.auth.Auth; import javax.inject.Inject; +import org.joda.time.DateTime; /** - * A mapreduce that re-saves all EppResources without otherwise modifying them. + * A mapreduce that re-saves all EppResources, projecting them forward to the current time. * *
This is useful for completing data migrations on EppResource fields that are accomplished * with @OnSave or @OnLoad annotations, and also guarantees that all EppResources will get fresh - * commit logs (for backup purposes). + * commit logs (for backup purposes). Additionally, pending actions such as transfers or grace + * periods that are past their effective time will be resolved. * *
Because there are no auth settings in the {@link Action} annotation, this command can only be * run internally, or by pretending to be internal by setting the X-AppEngine-QueueName header, @@ -50,7 +53,6 @@ public class ResaveAllEppResourcesAction implements Runnable { @Inject Response response; @Inject ResaveAllEppResourcesAction() {} - @SuppressWarnings("unchecked") @Override public void run() { response.sendJavaScriptRedirect(createJobPath(mrRunner @@ -73,7 +75,9 @@ public class ResaveAllEppResourcesAction implements Runnable { ofy().transact(new VoidWork() { @Override public void vrun() { - ofy().save().entity(ofy().load().key(resourceKey).now()).now(); + EppResource projectedResource = + ofy().load().key(resourceKey).now().cloneProjectedAtTime(DateTime.now(UTC)); + ofy().save().entity(projectedResource).now(); }}); getContext().incrementCounter(String.format("%s entities re-saved", resourceKey.getKind())); } diff --git a/javatests/google/registry/tools/server/ResaveAllEppResourcesActionTest.java b/javatests/google/registry/tools/server/ResaveAllEppResourcesActionTest.java index 766fcc0a3..24ffacf9a 100644 --- a/javatests/google/registry/tools/server/ResaveAllEppResourcesActionTest.java +++ b/javatests/google/registry/tools/server/ResaveAllEppResourcesActionTest.java @@ -17,8 +17,11 @@ package google.registry.tools.server; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.persistActiveContact; +import static google.registry.testing.DatastoreHelper.persistContactWithPendingTransfer; +import static org.joda.time.DateTimeZone.UTC; import google.registry.model.contact.ContactResource; +import google.registry.model.transfer.TransferStatus; import google.registry.testing.FakeResponse; import google.registry.testing.mapreduce.MapreduceTestCase; import org.joda.time.DateTime; @@ -55,4 +58,25 @@ public class ResaveAllEppResourcesActionTest assertThat(ofy().load().entity(contact).now().getUpdateAutoTimestamp().getTimestamp()) .isGreaterThan(creationTime); } + + @Test + public void test_mapreduceResolvesPendingTransfer() throws Exception { + DateTime now = DateTime.now(UTC); + // Set up a contact with a transfer that implicitly completed five days ago. + ContactResource contact = + persistContactWithPendingTransfer( + persistActiveContact("meh789"), + now.minusDays(10), + now.minusDays(10), + now.minusDays(10)); + assertThat(contact.getTransferData().getTransferStatus()).isEqualTo(TransferStatus.PENDING); + runMapreduce(); + + ofy().clearSessionCache(); + // The transfer should be effective after the contact is re-saved, as it should've been + // projected to the current time. + ContactResource resavedContact = ofy().load().entity(contact).now(); + assertThat(resavedContact.getTransferData().getTransferStatus()) + .isEqualTo(TransferStatus.SERVER_APPROVED); + } }