diff --git a/java/google/registry/flows/Flow.java b/java/google/registry/flows/Flow.java index 154b32ff8..c19ab5546 100644 --- a/java/google/registry/flows/Flow.java +++ b/java/google/registry/flows/Flow.java @@ -14,90 +14,24 @@ package google.registry.flows; -import com.google.common.collect.ImmutableList; -import google.registry.model.eppcommon.Trid; -import google.registry.model.eppinput.EppInput; -import google.registry.model.eppoutput.EppOutput; -import google.registry.model.eppoutput.EppResponse; -import google.registry.model.eppoutput.EppResponse.ResponseData; -import google.registry.model.eppoutput.EppResponse.ResponseExtension; -import google.registry.model.eppoutput.Result; -import google.registry.model.poll.MessageQueueInfo; -import javax.annotation.Nullable; -import org.joda.time.DateTime; +import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting; /** - * An abstract EPP flow. + * An Extensible Provisioning Protocol flow. * - *

This class also contains static methods for loading an appropriate flow based on model - * classes. + *

A "flow" is our word for a "command", since we've overloaded the word "command" to mean the + * command objects and also the CLI operation classes. */ -public abstract class Flow { - - protected EppInput eppInput; - protected SessionMetadata sessionMetadata; - protected TransportCredentials credentials; - protected Trid trid; - protected DateTime now; - - /** Whether this flow is being run in a superuser mode that can skip some checks. */ - protected boolean isSuperuser; - - /** Flows can override this for custom initialization. */ - @SuppressWarnings("unused") - protected void initFlow() throws EppException {} - - /** Execute the business logic for this flow. */ - protected abstract EppOutput run() throws EppException; - - protected EppOutput createOutput(Result.Code code) { - return createOutput(code, null); - } - - protected EppOutput createOutput(Result.Code code, ResponseData responseData) { - return createOutput(code, responseData, null); - } - - protected EppOutput createOutput( - Result.Code code, - @Nullable ResponseData responseData, - @Nullable ImmutableList extensions) { - return createOutput( - code, responseData == null ? null : ImmutableList.of(responseData), extensions, null); - } - - protected EppOutput createOutput( - Result.Code code, - @Nullable ImmutableList responseData, - @Nullable ImmutableList responseExtensions, - @Nullable MessageQueueInfo messageQueueInfo) { - return EppOutput.create(new EppResponse.Builder() - .setTrid(trid) - .setResult(Result.create(code)) - .setMessageQueueInfo(messageQueueInfo) - .setResData(responseData) - .setExtensions(responseExtensions) - .build()); - } +public interface Flow { /** - * Using an init function instead of a constructor avoids duplicating constructors across the - * entire hierarchy of flow classes + * Executes an EPP "flow" and returns a response object (or in the specific case of the "hello" + * flow a greeting object) that can be converted to XML and returned to the caller. + * + *

Flows should have {@link #run} called once per instance. If a flow needs to be retried, a + * new instance should be created. + * + *

Flows should get all of their parameters via injection off of {@link FlowComponent}. */ - public final Flow init( - EppInput eppInput, - Trid trid, - SessionMetadata sessionMetadata, - TransportCredentials credentials, - boolean isSuperuser, - DateTime now) throws EppException { - this.eppInput = eppInput; - this.trid = trid; - this.sessionMetadata = sessionMetadata; - this.credentials = credentials; - this.now = now; - this.isSuperuser = isSuperuser; - initFlow(); - return this; - } + ResponseOrGreeting run() throws EppException; } diff --git a/java/google/registry/flows/FlowModule.java b/java/google/registry/flows/FlowModule.java index bfe92367d..72b556e29 100644 --- a/java/google/registry/flows/FlowModule.java +++ b/java/google/registry/flows/FlowModule.java @@ -31,6 +31,8 @@ import google.registry.model.eppinput.EppInput.Poll; import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; +import google.registry.model.eppoutput.EppResponse; +import google.registry.model.eppoutput.Result; import google.registry.model.reporting.HistoryEntry; import java.lang.annotation.Documented; import javax.inject.Qualifier; @@ -244,6 +246,19 @@ public class FlowModule { return historyBuilder; } + /** + * Provides a partially filled in {@link EppResponse} builder. + * + *

This is not marked with {@link FlowScope} so that each retry gets a fresh one. Otherwise, + * the fact that the builder is one-use would cause NPEs. + */ + @Provides + static EppResponse.Builder provideEppResponseBuilder(Trid trid) { + return new EppResponse.Builder() + .setTrid(trid) + .setResultFromCode(Result.Code.SUCCESS); // Default to success. + } + /** Wrapper class to carry an {@link EppException} to the calling code. */ static class EppExceptionInProviderException extends RuntimeException { EppExceptionInProviderException(EppException exception) { diff --git a/java/google/registry/flows/FlowRunner.java b/java/google/registry/flows/FlowRunner.java index bce5bf9f7..0956f7f91 100644 --- a/java/google/registry/flows/FlowRunner.java +++ b/java/google/registry/flows/FlowRunner.java @@ -28,14 +28,11 @@ import google.registry.flows.FlowModule.InputXml; import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.Transactional; import google.registry.model.eppcommon.Trid; -import google.registry.model.eppinput.EppInput; import google.registry.model.eppoutput.EppOutput; import google.registry.monitoring.whitebox.EppMetric; -import google.registry.util.Clock; import google.registry.util.FormattingLogger; import javax.inject.Inject; import javax.inject.Provider; -import org.joda.time.DateTime; import org.json.simple.JSONValue; /** Run a flow, either transactionally or not, with logging and retrying as needed. */ @@ -56,9 +53,7 @@ public class FlowRunner { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); @Inject @ClientId String clientId; - @Inject Clock clock; @Inject TransportCredentials credentials; - @Inject EppInput eppInput; @Inject EppRequestSource eppRequestSource; @Inject Provider flowProvider; @Inject @InputXml byte[] inputXmlBytes; @@ -99,7 +94,7 @@ public class FlowRunner { "xmlBytes", xmlBase64))); if (!isTransactional) { metric.incrementAttempts(); - return createAndInitFlow(clock.nowUtc()).run(); + return EppOutput.create(flowProvider.get().run()); } try { return ofy().transact(new Work() { @@ -107,7 +102,7 @@ public class FlowRunner { public EppOutput run() { metric.incrementAttempts(); try { - EppOutput output = createAndInitFlow(ofy().getTransactionTime()).run(); + EppOutput output = EppOutput.create(flowProvider.get().run()); if (isDryRun) { throw new DryRunException(output); } @@ -127,15 +122,6 @@ public class FlowRunner { } } - private Flow createAndInitFlow(DateTime now) throws EppException { - return flowProvider.get().init( - eppInput, - trid, - sessionMetadata, - credentials, - isSuperuser, - now); - } /** Exception for canceling a transaction while capturing what the output would have been. */ private static class DryRunException extends RuntimeException { final EppOutput output; diff --git a/java/google/registry/flows/TransactionalFlow.java b/java/google/registry/flows/TransactionalFlow.java index 0c3aa089d..a228cca57 100644 --- a/java/google/registry/flows/TransactionalFlow.java +++ b/java/google/registry/flows/TransactionalFlow.java @@ -15,9 +15,9 @@ package google.registry.flows; /** - * Marker interface indicating that a {@link Flow} needs to be run transactionally. + * Interface for a {@link Flow} that needs to be run transactionally. * - *

Any flow that mutates the datastore should be tagged with this so that {@link FlowRunner} will - * know how to run it. + *

Any flow that mutates the datastore should implement this so that {@link FlowRunner} will know + * how to run it. */ -public interface TransactionalFlow {} +public interface TransactionalFlow extends Flow {} diff --git a/java/google/registry/flows/contact/ContactCheckFlow.java b/java/google/registry/flows/contact/ContactCheckFlow.java index 4897837db..0b3329347 100644 --- a/java/google/registry/flows/contact/ContactCheckFlow.java +++ b/java/google/registry/flows/contact/ContactCheckFlow.java @@ -17,7 +17,6 @@ package google.registry.flows.contact; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.model.EppResourceUtils.checkResourcesExist; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.collect.ImmutableList; import google.registry.config.ConfigModule.Config; @@ -30,7 +29,8 @@ import google.registry.model.contact.ContactResource; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CheckData.ContactCheck; import google.registry.model.eppoutput.CheckData.ContactCheckData; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; +import google.registry.util.Clock; import java.util.List; import java.util.Set; import javax.inject.Inject; @@ -42,26 +42,28 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} */ -public final class ContactCheckFlow extends Flow { +public final class ContactCheckFlow implements Flow { @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @Inject ExtensionManager extensionManager; + @Inject Clock clock; @Inject @Config("maxChecks") int maxChecks; + @Inject EppResponse.Builder responseBuilder; @Inject ContactCheckFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); - Set existingIds = checkResourcesExist(ContactResource.class, targetIds, now); + Set existingIds = checkResourcesExist(ContactResource.class, targetIds, clock.nowUtc()); ImmutableList.Builder checks = new ImmutableList.Builder<>(); for (String id : targetIds) { boolean unused = !existingIds.contains(id); checks.add(ContactCheck.create(unused, id, unused ? null : "In use")); } - return createOutput(SUCCESS, ContactCheckData.create(checks.build())); + return responseBuilder.setResData(ContactCheckData.create(checks.build())).build(); } } diff --git a/java/google/registry/flows/contact/ContactCreateFlow.java b/java/google/registry/flows/contact/ContactCreateFlow.java index 592472e42..ecf4d1d38 100644 --- a/java/google/registry/flows/contact/ContactCreateFlow.java +++ b/java/google/registry/flows/contact/ContactCreateFlow.java @@ -19,13 +19,11 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; import static google.registry.model.EppResourceUtils.createContactHostRoid; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -35,12 +33,13 @@ import google.registry.model.contact.ContactResource.Builder; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.ContactCreateData; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.ofy.ObjectifyService; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that creates a new contact. @@ -49,21 +48,23 @@ import javax.inject.Inject; * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} */ -public final class ContactCreateFlow extends Flow implements TransactionalFlow { +public final class ContactCreateFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject ContactCreateFlow() {} @Override - protected final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); Create command = (Create) resourceCommand; + DateTime now = ofy().getTransactionTime(); verifyResourceDoesNotExist(ContactResource.class, targetId, now); ContactResource newContact = new Builder() .setContactId(targetId) @@ -90,6 +91,8 @@ public final class ContactCreateFlow extends Flow implements TransactionalFlow { historyBuilder.build(), ForeignKeyIndex.create(newContact, newContact.getDeletionTime()), EppResourceIndex.create(Key.create(newContact))); - return createOutput(SUCCESS, ContactCreateData.create(newContact.getContactId(), now)); + return responseBuilder + .setResData(ContactCreateData.create(newContact.getContactId(), now)) + .build(); } } diff --git a/java/google/registry/flows/contact/ContactDeleteFlow.java b/java/google/registry/flows/contact/ContactDeleteFlow.java index 0e057f29b..13600b7f7 100644 --- a/java/google/registry/flows/contact/ContactDeleteFlow.java +++ b/java/google/registry/flows/contact/ContactDeleteFlow.java @@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowEnqueuer; @@ -39,9 +39,10 @@ import google.registry.model.domain.DomainBase; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that deletes a contact. @@ -57,7 +58,7 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} * @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} */ -public final class ContactDeleteFlow extends Flow implements TransactionalFlow { +public final class ContactDeleteFlow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.LINKED, @@ -75,16 +76,19 @@ public final class ContactDeleteFlow extends Flow implements TransactionalFlow { @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject Optional authInfo; @Inject HistoryEntry.Builder historyBuilder; @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; + @Inject EppResponse.Builder responseBuilder; @Inject ContactDeleteFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); failfastForAsyncDelete(targetId, now, ContactResource.class, GET_REFERENCED_CONTACTS); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyNoDisallowedStatuses(existingContact, DISALLOWED_STATUSES); @@ -100,6 +104,6 @@ public final class ContactDeleteFlow extends Flow implements TransactionalFlow { .setModificationTime(now) .setParent(Key.create(existingContact)); ofy().save().entities(newContact, historyBuilder.build()); - return createOutput(SUCCESS_WITH_ACTION_PENDING); + return responseBuilder.setResultFromCode(SUCCESS_WITH_ACTION_PENDING).build(); } } diff --git a/java/google/registry/flows/contact/ContactInfoFlow.java b/java/google/registry/flows/contact/ContactInfoFlow.java index efc06b677..194a5cc87 100644 --- a/java/google/registry/flows/contact/ContactInfoFlow.java +++ b/java/google/registry/flows/contact/ContactInfoFlow.java @@ -18,7 +18,6 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; @@ -28,8 +27,10 @@ import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; +import google.registry.util.Clock; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that returns information about a contact. @@ -41,16 +42,19 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} */ -public final class ContactInfoFlow extends Flow { +public final class ContactInfoFlow implements Flow { @Inject ExtensionManager extensionManager; + @Inject Clock clock; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject Optional authInfo; + @Inject EppResponse.Builder responseBuilder; @Inject ContactInfoFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { + DateTime now = clock.nowUtc(); extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); @@ -58,6 +62,6 @@ public final class ContactInfoFlow extends Flow { if (!clientId.equals(contact.getCurrentSponsorClientId()) && !authInfo.isPresent()) { contact = contact.asBuilder().setAuthInfo(null).build(); } - return createOutput(SUCCESS, cloneResourceWithLinkedStatus(contact, now)); + return responseBuilder.setResData(cloneResourceWithLinkedStatus(contact, now)).build(); } } diff --git a/java/google/registry/flows/contact/ContactTransferApproveFlow.java b/java/google/registry/flows/contact/ContactTransferApproveFlow.java index 184a2b5bd..50002098c 100644 --- a/java/google/registry/flows/contact/ContactTransferApproveFlow.java +++ b/java/google/registry/flows/contact/ContactTransferApproveFlow.java @@ -21,14 +21,12 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -37,12 +35,13 @@ import google.registry.model.contact.ContactResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that approves a pending transfer on a contact. @@ -57,7 +56,7 @@ import javax.inject.Inject; * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException} */ -public final class ContactTransferApproveFlow extends Flow implements TransactionalFlow { +public final class ContactTransferApproveFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; @@ -65,6 +64,7 @@ public final class ContactTransferApproveFlow extends Flow implements Transactio @Inject @TargetId String targetId; @Inject Optional authInfo; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject ContactTransferApproveFlow() {} /** @@ -72,10 +72,11 @@ public final class ContactTransferApproveFlow extends Flow implements Transactio * {@link ContactResource#cloneProjectedAtTime} which handles implicit server approvals. */ @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); TransferData transferData = existingContact.getTransferData(); @@ -97,6 +98,8 @@ public final class ContactTransferApproveFlow extends Flow implements Transactio // Delete the billing event and poll messages that were written in case the transfer would have // been implicitly server approved. ofy().delete().keys(transferData.getServerApproveEntities()); - return createOutput(SUCCESS, createTransferResponse(targetId, newContact.getTransferData())); + return responseBuilder + .setResData(createTransferResponse(targetId, newContact.getTransferData())) + .build(); } } diff --git a/java/google/registry/flows/contact/ContactTransferCancelFlow.java b/java/google/registry/flows/contact/ContactTransferCancelFlow.java index b7413c3da..5e61789cc 100644 --- a/java/google/registry/flows/contact/ContactTransferCancelFlow.java +++ b/java/google/registry/flows/contact/ContactTransferCancelFlow.java @@ -20,14 +20,12 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.contact.ContactFlowUtils.createLosingTransferPollMessage; import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -37,12 +35,13 @@ import google.registry.model.contact.ContactResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that cancels a pending transfer on a contact. @@ -57,7 +56,7 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link google.registry.flows.exceptions.NotTransferInitiatorException} */ -public final class ContactTransferCancelFlow extends Flow implements TransactionalFlow { +public final class ContactTransferCancelFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; @@ -65,13 +64,15 @@ public final class ContactTransferCancelFlow extends Flow implements Transaction @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject ContactTransferCancelFlow() {} @Override - protected final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); TransferData transferData = existingContact.getTransferData(); @@ -95,6 +96,8 @@ public final class ContactTransferCancelFlow extends Flow implements Transaction // Delete the billing event and poll messages that were written in case the transfer would have // been implicitly server approved. ofy().delete().keys(transferData.getServerApproveEntities()); - return createOutput(SUCCESS, createTransferResponse(targetId, newContact.getTransferData())); + return responseBuilder + .setResData(createTransferResponse(targetId, newContact.getTransferData())) + .build(); } } diff --git a/java/google/registry/flows/contact/ContactTransferQueryFlow.java b/java/google/registry/flows/contact/ContactTransferQueryFlow.java index 30c6c1828..5bcd0ad19 100644 --- a/java/google/registry/flows/contact/ContactTransferQueryFlow.java +++ b/java/google/registry/flows/contact/ContactTransferQueryFlow.java @@ -18,7 +18,6 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; @@ -30,7 +29,8 @@ import google.registry.flows.exceptions.NoTransferHistoryToQueryException; import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; +import google.registry.util.Clock; import javax.inject.Inject; /** @@ -48,19 +48,22 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException} * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} */ -public final class ContactTransferQueryFlow extends Flow { +public final class ContactTransferQueryFlow implements Flow { @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject ContactTransferQueryFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); - ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); + ContactResource contact = + loadAndVerifyExistence(ContactResource.class, targetId, clock.nowUtc()); verifyOptionalAuthInfoForResource(authInfo, contact); // Most of the fields on the transfer response are required, so there's no way to return valid // XML if the object has never been transferred (and hence the fields aren't populated). @@ -74,6 +77,8 @@ public final class ContactTransferQueryFlow extends Flow { && !clientId.equals(contact.getTransferData().getLosingClientId())) { throw new NotAuthorizedToViewTransferException(); } - return createOutput(SUCCESS, createTransferResponse(targetId, contact.getTransferData())); + return responseBuilder + .setResData(createTransferResponse(targetId, contact.getTransferData())) + .build(); } } diff --git a/java/google/registry/flows/contact/ContactTransferRejectFlow.java b/java/google/registry/flows/contact/ContactTransferRejectFlow.java index 3ca325099..53efcac06 100644 --- a/java/google/registry/flows/contact/ContactTransferRejectFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRejectFlow.java @@ -21,14 +21,12 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -36,12 +34,13 @@ import google.registry.flows.exceptions.NotPendingTransferException; import google.registry.model.contact.ContactResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that rejects a pending transfer on a contact. @@ -56,20 +55,22 @@ import javax.inject.Inject; * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException} */ -public final class ContactTransferRejectFlow extends Flow implements TransactionalFlow { +public final class ContactTransferRejectFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject ContactTransferRejectFlow() {} @Override - protected final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); TransferData transferData = existingContact.getTransferData(); @@ -90,6 +91,8 @@ public final class ContactTransferRejectFlow extends Flow implements Transaction // Delete the billing event and poll messages that were written in case the transfer would have // been implicitly server approved. ofy().delete().keys(transferData.getServerApproveEntities()); - return createOutput(SUCCESS, createTransferResponse(targetId, newContact.getTransferData())); + return responseBuilder + .setResData(createTransferResponse(targetId, newContact.getTransferData())) + .build(); } } diff --git a/java/google/registry/flows/contact/ContactTransferRequestFlow.java b/java/google/registry/flows/contact/ContactTransferRequestFlow.java index 53b2bac50..1975d2509 100644 --- a/java/google/registry/flows/contact/ContactTransferRequestFlow.java +++ b/java/google/registry/flows/contact/ContactTransferRequestFlow.java @@ -30,7 +30,6 @@ import com.googlecode.objectify.Key; import google.registry.config.ConfigModule.Config; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -40,7 +39,8 @@ import google.registry.model.contact.ContactResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppcommon.Trid; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferData; @@ -64,7 +64,7 @@ import org.joda.time.Duration; * @error {@link google.registry.flows.exceptions.MissingTransferRequestAuthInfoException} * @error {@link google.registry.flows.exceptions.ObjectAlreadySponsoredException} */ -public final class ContactTransferRequestFlow extends Flow implements TransactionalFlow { +public final class ContactTransferRequestFlow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.CLIENT_TRANSFER_PROHIBITED, @@ -77,13 +77,16 @@ public final class ContactTransferRequestFlow extends Flow implements Transactio @Inject @TargetId String targetId; @Inject @Config("contactAutomaticTransferLength") Duration automaticTransferLength; @Inject HistoryEntry.Builder historyBuilder; + @Inject Trid trid; + @Inject EppResponse.Builder responseBuilder; @Inject ContactTransferRequestFlow() {} @Override - protected final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(gainingClientId); + DateTime now = ofy().getTransactionTime(); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyRequiredAuthInfoForResourceTransfer(authInfo, existingContact); // Verify that the resource does not already have a pending transfer. @@ -138,9 +141,10 @@ public final class ContactTransferRequestFlow extends Flow implements Transactio requestPollMessage, serverApproveGainingPollMessage, serverApproveLosingPollMessage); - return createOutput( - SUCCESS_WITH_ACTION_PENDING, - createTransferResponse(targetId, newContact.getTransferData())); + return responseBuilder + .setResultFromCode(SUCCESS_WITH_ACTION_PENDING) + .setResData(createTransferResponse(targetId, newContact.getTransferData())) + .build(); } } diff --git a/java/google/registry/flows/contact/ContactUpdateFlow.java b/java/google/registry/flows/contact/ContactUpdateFlow.java index 7a8c032d4..21ebcec76 100644 --- a/java/google/registry/flows/contact/ContactUpdateFlow.java +++ b/java/google/registry/flows/contact/ContactUpdateFlow.java @@ -24,7 +24,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; @@ -32,8 +31,8 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; @@ -46,10 +45,11 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.reporting.HistoryEntry; import javax.annotation.Nullable; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that updates a contact. @@ -63,7 +63,7 @@ import javax.inject.Inject; * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} */ -public final class ContactUpdateFlow extends Flow implements TransactionalFlow { +public final class ContactUpdateFlow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -79,15 +79,18 @@ public final class ContactUpdateFlow extends Flow implements TransactionalFlow { @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject ContactUpdateFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); Update command = (Update) resourceCommand; + DateTime now = ofy().getTransactionTime(); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingContact); ImmutableSet statusToRemove = command.getInnerRemove().getStatusValues(); @@ -146,7 +149,7 @@ public final class ContactUpdateFlow extends Flow implements TransactionalFlow { validateAsciiPostalInfo(newContact.getInternationalizedPostalInfo()); validateContactAgainstPolicy(newContact); ofy().save().entities(newContact, historyBuilder.build()); - return createOutput(SUCCESS); + return responseBuilder.build(); } /** Return the first non-null param, or null if both are null. */ diff --git a/java/google/registry/flows/domain/ClaimsCheckFlow.java b/java/google/registry/flows/domain/ClaimsCheckFlow.java index cd02db10b..a81549c4b 100644 --- a/java/google/registry/flows/domain/ClaimsCheckFlow.java +++ b/java/google/registry/flows/domain/ClaimsCheckFlow.java @@ -22,7 +22,6 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWit import static google.registry.flows.domain.DomainFlowUtils.verifyClaimsPeriodNotEnded; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.model.domain.launch.LaunchPhase.CLAIMS; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -33,20 +32,23 @@ import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.launch.LaunchCheckExtension; import google.registry.model.domain.launch.LaunchCheckResponseExtension; import google.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheck; import google.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheckName; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.tmch.ClaimsListShard; +import google.registry.util.Clock; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that checks whether strings are trademarked. @@ -58,16 +60,19 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link ClaimsCheckNotAllowedInSunrise} */ -public final class ClaimsCheckFlow extends Flow { +public final class ClaimsCheckFlow implements Flow { @Inject ExtensionManager extensionManager; @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; + @Inject @Superuser boolean isSuperuser; + @Inject Clock clock; @Inject @Config("maxChecks") int maxChecks; + @Inject EppResponse.Builder responseBuilder; @Inject ClaimsCheckFlow() {} @Override - public EppOutput run() throws EppException { + public EppResponse run() throws EppException { extensionManager.register(LaunchCheckExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); @@ -84,6 +89,7 @@ public final class ClaimsCheckFlow extends Flow { checkAllowedAccessToTld(clientId, tld); Registry registry = Registry.get(tld); if (!isSuperuser) { + DateTime now = clock.nowUtc(); verifyNotInPredelegation(registry, now); if (registry.getTldState(now) == TldState.SUNRISE) { throw new ClaimsCheckNotAllowedInSunrise(); @@ -96,10 +102,9 @@ public final class ClaimsCheckFlow extends Flow { LaunchCheck.create( LaunchCheckName.create(claimKey != null, targetId), claimKey)); } - return createOutput( - SUCCESS, - null, - ImmutableList.of(LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build()))); + return responseBuilder + .setOnlyExtension(LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build())) + .build(); } /** Claims checks are not allowed during sunrise. */ diff --git a/java/google/registry/flows/domain/DomainAllocateFlow.java b/java/google/registry/flows/domain/DomainAllocateFlow.java index 6ec78e0c9..4ca97cf6b 100644 --- a/java/google/registry/flows/domain/DomainAllocateFlow.java +++ b/java/google/registry/flows/domain/DomainAllocateFlow.java @@ -48,8 +48,8 @@ import google.registry.flows.EppException.AuthorizationErrorException; import google.registry.flows.EppException.ObjectDoesNotExistException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; @@ -73,10 +73,10 @@ import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.DomainCreateData; -import google.registry.model.eppoutput.EppOutput; -import google.registry.model.eppoutput.Result; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.ofy.ObjectifyService; @@ -97,7 +97,7 @@ import org.joda.time.DateTime; * @error {@link DomainAllocateFlow.MissingApplicationException} * @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException} */ -public class DomainAllocateFlow extends Flow implements TransactionalFlow { +public class DomainAllocateFlow implements TransactionalFlow { private static final String COLLISION_MESSAGE = "Domain on the name collision list was allocated. But by policy, the domain will not be " @@ -109,11 +109,14 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppInput eppInput; + @Inject EppResponse.Builder responseBuilder; @Inject DomainAllocateFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register( SecDnsCreateExtension.class, FlagsCreateCommandExtension.class, @@ -123,6 +126,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { extensionManager.validate(); validateClientIsLoggedIn(clientId); verifyIsSuperuser(); + DateTime now = ofy().getTransactionTime(); Create command = cloneAndLinkReferences((Create) resourceCommand, now); failfastForCreate(targetId, now); verifyResourceDoesNotExist(DomainResource.class, targetId, now); @@ -137,13 +141,14 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { boolean isSunrushAddGracePeriod = isNullOrEmpty(command.getNameservers()); AllocateCreateExtension allocateCreate = eppInput.getSingleExtension(AllocateCreateExtension.class); - DomainApplication application = loadAndValidateApplication(allocateCreate.getApplicationRoid()); + DomainApplication application = + loadAndValidateApplication(allocateCreate.getApplicationRoid(), now); String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr()); ImmutableSet.Builder entitiesToSave = new ImmutableSet.Builder<>(); - HistoryEntry historyEntry = buildHistory(repoId, period); + HistoryEntry historyEntry = buildHistory(repoId, period, now); entitiesToSave.add(historyEntry); ImmutableSet billsAndPolls = createBillingEventsAndPollMessages( - domainName, application, historyEntry, isSunrushAddGracePeriod, registry, years); + domainName, application, historyEntry, isSunrushAddGracePeriod, registry, now, years); entitiesToSave.addAll(billsAndPolls); DateTime registrationExpirationTime = leapSafeAddYears(now, years); DomainResource newDomain = new DomainResource.Builder() @@ -173,21 +178,21 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { .build(); entitiesToSave.add( newDomain, - buildApplicationHistory(application), + buildApplicationHistory(application, now), updateApplication(application), ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()), EppResourceIndex.create(Key.create(newDomain))); // Anchor tenant registrations override LRP. String authInfoToken = authInfo.getPw().getValue(); - if (hasLrpToken(domainName, registry, authInfoToken)) { + if (hasLrpToken(domainName, registry, authInfoToken, now)) { entitiesToSave.add(prepareMarkedLrpTokenEntity(authInfoToken, domainName, historyEntry)); } ofy().save().entities(entitiesToSave.build()); enqueueTasks(allocateCreate, newDomain); - return createOutput( - Result.Code.SUCCESS, - DomainCreateData.create(targetId, now, registrationExpirationTime), - createResponseExtensions(registry, years)); + return responseBuilder + .setResData(DomainCreateData.create(targetId, now, registrationExpirationTime)) + .setExtensions(createResponseExtensions(now, registry, years)) + .build(); } private T getOnly( @@ -201,7 +206,8 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { } } - private DomainApplication loadAndValidateApplication(String applicationRoid) throws EppException { + private DomainApplication loadAndValidateApplication( + String applicationRoid, DateTime now) throws EppException { DomainApplication application = loadDomainApplication(applicationRoid, now); if (application == null) { throw new MissingApplicationException(applicationRoid); @@ -212,7 +218,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { return application; } - private HistoryEntry buildHistory(String repoId, Period period) { + private HistoryEntry buildHistory(String repoId, Period period, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_ALLOCATE) .setPeriod(period) @@ -227,12 +233,13 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { HistoryEntry historyEntry, boolean isSunrushAddGracePeriod, Registry registry, + DateTime now, int years) { DateTime registrationExpirationTime = leapSafeAddYears(now, years); BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent( - application, historyEntry, isSunrushAddGracePeriod, registry, years); + application, historyEntry, isSunrushAddGracePeriod, registry, now, years); PollMessage.OneTime oneTimePollMessage = - createOneTimePollMessage(application, historyEntry, getReservationType(domainName)); + createOneTimePollMessage(application, historyEntry, getReservationType(domainName), now); // Create a new autorenew billing event and poll message starting at the expiration time. BillingEvent.Recurring autorenewBillingEvent = createAutorenewBillingEvent(historyEntry, registrationExpirationTime); @@ -250,6 +257,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { HistoryEntry historyEntry, boolean isSunrushAddGracePeriod, Registry registry, + DateTime now, int years) { return new BillingEvent.OneTime.Builder() .setReason(Reason.CREATE) @@ -296,7 +304,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { .build(); } - private GracePeriod createGracePeriod(boolean isSunrushAddGracePeriod, + private static GracePeriod createGracePeriod(boolean isSunrushAddGracePeriod, BillingEvent.OneTime oneTimeBillingEvent) { return GracePeriod.forBillingEvent( isSunrushAddGracePeriod ? GracePeriodStatus.SUNRUSH_ADD : GracePeriodStatus.ADD, @@ -304,7 +312,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { } /** Create a history entry (with no xml or trid) to record that we updated the application. */ - private HistoryEntry buildApplicationHistory(DomainApplication application) { + private static HistoryEntry buildApplicationHistory(DomainApplication application, DateTime now) { return new HistoryEntry.Builder() .setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE) .setParent(application) @@ -324,7 +332,10 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { /** Create a poll message informing the registrar that the application status was updated. */ private PollMessage.OneTime createOneTimePollMessage( - DomainApplication application, HistoryEntry historyEntry, ReservationType reservationType) { + DomainApplication application, + HistoryEntry historyEntry, + ReservationType reservationType, + DateTime now) { return new PollMessage.OneTime.Builder() .setClientId(historyEntry.getClientId()) .setEventTime(now) @@ -345,7 +356,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { } private boolean hasLrpToken( - InternetDomainName domainName, Registry registry, String authInfoToken) { + InternetDomainName domainName, Registry registry, String authInfoToken, DateTime now) { return registry.getLrpPeriod().contains(now) && !matchesAnchorTenantReservation(domainName, authInfoToken); } @@ -360,7 +371,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow { } private ImmutableList createResponseExtensions( - Registry registry, int years) throws EppException { + DateTime now, Registry registry, int years) throws EppException { EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice( registry, targetId, clientId, now, years, eppInput); FeeTransformCommandExtension feeCreate = diff --git a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java index 716e5fa20..377f6053a 100644 --- a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java @@ -39,7 +39,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifySignedMarks; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.model.EppResourceUtils.createDomainRoid; import static google.registry.model.domain.fee.Fee.FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation; @@ -56,8 +55,8 @@ import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.ObjectAlreadyExistsException; import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; @@ -76,9 +75,11 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppcommon.Trid; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.DomainCreateData; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.index.DomainApplicationIndex; import google.registry.model.index.EppResourceIndex; @@ -89,6 +90,7 @@ import google.registry.model.reporting.HistoryEntry; import google.registry.model.smd.AbstractSignedMark; import google.registry.model.smd.EncodedSignedMark; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that creates a new application for a domain resource. @@ -153,18 +155,22 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} * @error {@link DomainFlowUtils.UnsupportedMarkTypeException} */ -public final class DomainApplicationCreateFlow extends Flow implements TransactionalFlow { +public final class DomainApplicationCreateFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject AuthInfo authInfo; @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject Trid trid; + @Inject EppResponse.Builder responseBuilder; @Inject DomainApplicationCreateFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register( SecDnsCreateExtension.class, FlagsCreateCommandExtension.class, @@ -173,6 +179,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); Create command = cloneAndLinkReferences((Create) resourceCommand, now); failfastForCreate(targetId, now); // Fail if the domain is already registered (e.g. this is a landrush application but the domain @@ -193,13 +200,13 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti validateCreateCommandContactsAndNameservers(command, tld); LaunchCreateExtension launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class); if (launchCreate != null) { - validateLaunchCreateExtension(launchCreate, registry, domainName); + validateLaunchCreateExtension(launchCreate, registry, domainName, now); } boolean isAnchorTenant = matchesAnchorTenantReservation(domainName, authInfo.getPw().getValue()); if (!isSuperuser) { verifyPremiumNameIsNotBlocked(targetId, now, clientId); - prohibitLandrushIfExactlyOneSunrise(registry); + prohibitLandrushIfExactlyOneSunrise(registry, now); if (!isAnchorTenant) { boolean isSunriseApplication = !launchCreate.getSignedMarks().isEmpty(); verifyNotReserved(domainName, isSunriseApplication); @@ -236,7 +243,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti }}) .toList()) .build(); - HistoryEntry historyEntry = buildHistory(newApplication.getRepoId(), command.getPeriod()); + HistoryEntry historyEntry = buildHistory(newApplication.getRepoId(), command.getPeriod(), now); ImmutableSet.Builder entitiesToSave = new ImmutableSet.Builder<>(); handleExtraFlowLogic( registry.getTldStr(), command.getPeriod().getValue(), historyEntry, newApplication); @@ -252,16 +259,18 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry)); } ofy().save().entities(entitiesToSave.build()); - return createOutput( - SUCCESS, - DomainCreateData.create(targetId, now, null), - createResponseExtensions( - newApplication.getForeignKey(), launchCreate.getPhase(), feeCreate, commandOperations)); + return responseBuilder + .setResData(DomainCreateData.create(targetId, now, null)) + .setExtensions(createResponseExtensions( + newApplication.getForeignKey(), launchCreate.getPhase(), feeCreate, commandOperations)) + .build(); } private void validateLaunchCreateExtension( - LaunchCreateExtension launchCreate, Registry registry, InternetDomainName domainName) - throws EppException { + LaunchCreateExtension launchCreate, + Registry registry, + InternetDomainName domainName, + DateTime now) throws EppException { verifyNoCodeMarks(launchCreate); boolean hasClaimsNotice = launchCreate.getNotice() != null; if (hasClaimsNotice) { @@ -302,7 +311,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti * Prohibit creating a landrush application in LANDRUSH (but not in SUNRUSH) if there is exactly * one sunrise application for the same name. */ - private void prohibitLandrushIfExactlyOneSunrise(Registry registry) + private void prohibitLandrushIfExactlyOneSunrise(Registry registry, DateTime now) throws UncontestedSunriseApplicationBlockedInLandrushException { if (registry.getTldState(now) == TldState.LANDRUSH) { ImmutableSet applications = @@ -314,7 +323,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti } } - private HistoryEntry buildHistory(String repoId, Period period) { + private HistoryEntry buildHistory(String repoId, Period period, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_APPLICATION_CREATE) .setPeriod(period) @@ -349,7 +358,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti extraFlowLogic.get().performAdditionalApplicationCreateLogic( newApplication, clientId, - now, + newApplication.getCreationTime(), years, eppInput, historyEntry); diff --git a/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java b/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java index 6bb5949ef..bfdeca4d9 100644 --- a/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationDeleteFlow.java @@ -26,7 +26,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDoma import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchPhaseMatchesRegistryPhase; import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsLaunchFlows; import static google.registry.model.EppResourceUtils.loadDomainApplication; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; @@ -34,9 +33,9 @@ import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.model.domain.DomainApplication; @@ -44,11 +43,13 @@ import google.registry.model.domain.launch.LaunchDeleteExtension; import google.registry.model.domain.launch.LaunchPhase; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppinput.EppInput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that deletes a domain application. @@ -62,21 +63,25 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.LaunchPhaseMismatchException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainApplicationDeleteFlow extends Flow implements TransactionalFlow { +public final class DomainApplicationDeleteFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject @ApplicationId String applicationId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainApplicationDeleteFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class, LaunchDeleteExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); DomainApplication existingApplication = verifyExistence( DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); verifyApplicationDomainMatchesTargetId(existingApplication, targetId); @@ -105,7 +110,7 @@ public final class DomainApplicationDeleteFlow extends Flow implements Transacti updateForeignKeyIndexDeletionTime(newApplication); handlePendingTransferOnDelete(existingApplication, newApplication, now, historyEntry); ofy().save().entities(newApplication, historyEntry); - return createOutput(SUCCESS); + return responseBuilder.build(); } /** A sunrise application cannot be deleted during landrush. */ diff --git a/java/google/registry/flows/domain/DomainApplicationInfoFlow.java b/java/google/registry/flows/domain/DomainApplicationInfoFlow.java index 79601f601..ca2f9506c 100644 --- a/java/google/registry/flows/domain/DomainApplicationInfoFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationInfoFlow.java @@ -22,7 +22,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent; import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId; import static google.registry.model.EppResourceUtils.loadDomainApplication; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -39,12 +38,14 @@ import google.registry.model.domain.DomainCommand.Info; import google.registry.model.domain.launch.LaunchInfoExtension; import google.registry.model.domain.launch.LaunchInfoResponseExtension; import google.registry.model.eppcommon.AuthInfo; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.mark.Mark; import google.registry.model.smd.EncodedSignedMark; import google.registry.model.smd.SignedMark; +import google.registry.util.Clock; import javax.inject.Inject; /** @@ -59,18 +60,21 @@ import javax.inject.Inject; * @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException} * @error {@link MissingApplicationIdException} */ -public final class DomainApplicationInfoFlow extends Flow { +public final class DomainApplicationInfoFlow implements Flow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject @ApplicationId String applicationId; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject DomainApplicationInfoFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(LaunchInfoExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); @@ -78,7 +82,9 @@ public final class DomainApplicationInfoFlow extends Flow { throw new MissingApplicationIdException(); } DomainApplication application = verifyExistence( - DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); + DomainApplication.class, + applicationId, + loadDomainApplication(applicationId, clock.nowUtc())); verifyApplicationDomainMatchesTargetId(application, targetId); verifyOptionalAuthInfoForResource(authInfo, application); LaunchInfoExtension launchInfo = eppInput.getSingleExtension(LaunchInfoExtension.class); @@ -87,10 +93,10 @@ public final class DomainApplicationInfoFlow extends Flow { } // We don't support authInfo for applications, so if it's another registrar always fail. verifyResourceOwnership(clientId, application); - return createOutput( - SUCCESS, - getResourceInfo(application), - getDomainResponseExtensions(application, launchInfo)); + return responseBuilder + .setResData(getResourceInfo(application)) + .setExtensions(getDomainResponseExtensions(application, launchInfo)) + .build(); } DomainApplication getResourceInfo(DomainApplication application) { diff --git a/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java b/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java index 2d5d6a05d..d7349bd7c 100644 --- a/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java @@ -40,7 +40,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNot import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; import static google.registry.model.EppResourceUtils.loadDomainApplication; import static google.registry.model.domain.fee.Fee.FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; @@ -50,9 +49,9 @@ import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; @@ -66,10 +65,12 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that updates a domain application. @@ -101,7 +102,7 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException} * @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException} */ -public class DomainApplicationUpdateFlow extends Flow implements TransactionalFlow { +public class DomainApplicationUpdateFlow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -121,15 +122,18 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject @ApplicationId String applicationId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainApplicationUpdateFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register( LaunchUpdateExtension.class, MetadataExtension.class, @@ -137,6 +141,7 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); Update command = cloneAndLinkReferences((Update) resourceCommand, now); DomainApplication existingApplication = verifyExistence( DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); @@ -144,11 +149,11 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl verifyNoDisallowedStatuses(existingApplication, UPDATE_DISALLOWED_STATUSES); verifyOptionalAuthInfoForResource(authInfo, existingApplication); verifyUpdateAllowed(existingApplication, command); - HistoryEntry historyEntry = buildHistory(existingApplication); - DomainApplication newApplication = updateApplication(existingApplication, command); + HistoryEntry historyEntry = buildHistory(existingApplication, now); + DomainApplication newApplication = updateApplication(existingApplication, command, now); validateNewApplication(newApplication); ofy().save().entities(newApplication, historyEntry); - return createOutput(SUCCESS); + return responseBuilder.build(); } protected final void verifyUpdateAllowed( @@ -178,7 +183,7 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl tld, add.getNameserverFullyQualifiedHostNames()); } - private HistoryEntry buildHistory(DomainApplication existingApplication) { + private HistoryEntry buildHistory(DomainApplication existingApplication, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE) .setModificationTime(now) @@ -187,7 +192,7 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl } private DomainApplication updateApplication( - DomainApplication application, Update command) throws EppException { + DomainApplication application, Update command, DateTime now) throws EppException { AddRemove add = command.getInnerAdd(); AddRemove remove = command.getInnerRemove(); checkSameValuesNotAddedAndRemoved(add.getNameservers(), remove.getNameservers()); diff --git a/java/google/registry/flows/domain/DomainCheckFlow.java b/java/google/registry/flows/domain/DomainCheckFlow.java index d8909eb7a..062c7a333 100644 --- a/java/google/registry/flows/domain/DomainCheckFlow.java +++ b/java/google/registry/flows/domain/DomainCheckFlow.java @@ -24,12 +24,9 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWit import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.domain.fee.Fee.FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER; -import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName; import static google.registry.model.registry.label.ReservationType.UNRESERVED; import static google.registry.pricing.PricingEngineProxy.isDomainPremium; -import static google.registry.util.CollectionUtils.nullToEmpty; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; @@ -44,6 +41,7 @@ import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.DomainResource; @@ -51,19 +49,21 @@ import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtensionItem; import google.registry.model.domain.fee.FeeCheckResponseExtensionItem; import google.registry.model.domain.launch.LaunchCheckExtension; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CheckData.DomainCheck; import google.registry.model.eppoutput.CheckData.DomainCheckData; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.ReservationType; -import java.util.Collections; +import google.registry.util.Clock; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that checks whether a domain can be provisioned. @@ -90,7 +90,7 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.UnknownFeeCommandException} * @error {@link OnlyCheckedNamesCanBeFeeCheckedException} */ -public final class DomainCheckFlow extends Flow { +public final class DomainCheckFlow implements Flow { /** * The TLD states during which we want to report a domain with pending applications as @@ -101,18 +101,23 @@ public final class DomainCheckFlow extends Flow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject @ClientId String clientId; @Inject @Config("maxChecks") int maxChecks; + @Inject @Superuser boolean isSuperuser; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject DomainCheckFlow() {} @Override - public EppOutput run() throws EppException { + public EppResponse run() throws EppException { extensionManager.register(LaunchCheckExtension.class); extensionManager.registerAsGroup(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); + DateTime now = clock.nowUtc(); ImmutableMap.Builder domains = new ImmutableMap.Builder<>(); // Only check that the registrar has access to a TLD the first time it is encountered Set seenTlds = new HashSet<>(); @@ -133,17 +138,17 @@ public final class DomainCheckFlow extends Flow { Set existingIds = checkResourcesExist(DomainResource.class, targetIds, now); ImmutableList.Builder checks = new ImmutableList.Builder<>(); for (String targetId : targetIds) { - String message = getMessageForCheck(domainNames.get(targetId), existingIds); + String message = getMessageForCheck(domainNames.get(targetId), existingIds, now); checks.add(DomainCheck.create(message == null, targetId, message)); } - return createOutput( - SUCCESS, - DomainCheckData.create(checks.build()), - getResponseExtensions(domainNames)); + return responseBuilder + .setResData(DomainCheckData.create(checks.build())) + .setExtensions(getResponseExtensions(domainNames, now)) + .build(); } private String getMessageForCheck( - InternetDomainName domainName, Set existingIds) { + InternetDomainName domainName, Set existingIds, DateTime now) { if (existingIds.contains(domainName.toString())) { return "In use"; } @@ -161,9 +166,7 @@ public final class DomainCheckFlow extends Flow { if (reservationType == UNRESERVED && isDomainPremium(domainName.toString(), now) && registry.getPremiumPriceAckRequired() - && Collections.disjoint( - nullToEmpty(sessionMetadata.getServiceExtensionUris()), - FEE_EXTENSION_URIS)) { + && eppInput.getSingleExtension(FeeCheckCommandExtension.class) == null) { return "Premium names require EPP ext."; } return reservationType.getMessageForCheck(); @@ -172,7 +175,7 @@ public final class DomainCheckFlow extends Flow { /** Handle the fee check extension. */ private ImmutableList getResponseExtensions( - ImmutableMap domainNames) throws EppException { + ImmutableMap domainNames, DateTime now) throws EppException { FeeCheckCommandExtension feeCheck = eppInput.getFirstExtensionOfClasses(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); if (feeCheck == null) { diff --git a/java/google/registry/flows/domain/DomainCreateFlow.java b/java/google/registry/flows/domain/DomainCreateFlow.java index 812deaac3..78bd80a9e 100644 --- a/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/java/google/registry/flows/domain/DomainCreateFlow.java @@ -54,8 +54,8 @@ import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; @@ -78,10 +78,10 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.eppcommon.AuthInfo; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.DomainCreateData; -import google.registry.model.eppoutput.EppOutput; -import google.registry.model.eppoutput.Result; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.ofy.ObjectifyService; @@ -149,21 +149,24 @@ import org.joda.time.DateTime; * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} */ -public class DomainCreateFlow extends Flow implements TransactionalFlow { +public class DomainCreateFlow implements TransactionalFlow { private static final Set QLP_SMD_ALLOWED_STATES = Sets.immutableEnumSet(TldState.SUNRISE, TldState.SUNRUSH); @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject AuthInfo authInfo; @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainCreateFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register( SecDnsCreateExtension.class, FlagsCreateCommandExtension.class, @@ -172,6 +175,7 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); Create command = cloneAndLinkReferences((Create) resourceCommand, now); Period period = command.getPeriod(); verifyUnitIsYears(period); @@ -219,17 +223,17 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { verifyClaimsNoticeIfAndOnlyIfNeeded(domainName, hasSignedMarks, hasClaimsNotice); } verifyPremiumNameIsNotBlocked(targetId, now, clientId); - verifyNoOpenApplications(); + verifyNoOpenApplications(now); verifyIsGaOrIsSpecialCase(tldState, isAnchorTenant); } SecDnsCreateExtension secDnsCreate = validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr()); DateTime registrationExpirationTime = leapSafeAddYears(now, years); - HistoryEntry historyEntry = buildHistory(repoId, period); + HistoryEntry historyEntry = buildHistory(repoId, period, now); // Bill for the create. - BillingEvent.OneTime createBillingEvent = - createOneTimeBillingEvent(registry, isAnchorTenant, years, commandOperations, historyEntry); + BillingEvent.OneTime createBillingEvent = createOneTimeBillingEvent( + registry, isAnchorTenant, years, commandOperations, historyEntry, now); // Create a new autorenew billing event and poll message starting at the expiration time. BillingEvent.Recurring autorenewBillingEvent = createAutorenewBillingEvent(historyEntry, registrationExpirationTime); @@ -266,24 +270,24 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { .setContacts(command.getContacts()) .addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createBillingEvent)) .build(); - handleExtraFlowLogic(registry.getTldStr(), years, historyEntry, newDomain); + handleExtraFlowLogic(registry.getTldStr(), years, historyEntry, newDomain, now); entitiesToSave.add( newDomain, ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()), EppResourceIndex.create(Key.create(newDomain))); - + // Anchor tenant registrations override LRP, and landrush applications can skip it. // If a token is passed in outside of an LRP phase, it is simply ignored (i.e. never redeemed). - if (isLrpCreate(registry, isAnchorTenant)) { + if (isLrpCreate(registry, isAnchorTenant, now)) { entitiesToSave.add( prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry)); } enqueueTasks(hasSignedMarks, hasClaimsNotice, newDomain); ofy().save().entities(entitiesToSave.build()); - return createOutput( - Result.Code.SUCCESS, - DomainCreateData.create(targetId, now, registrationExpirationTime), - createResponseExtensions(feeCreate, commandOperations)); + return responseBuilder + .setResData(DomainCreateData.create(targetId, now, registrationExpirationTime)) + .setExtensions(createResponseExtensions(feeCreate, commandOperations)) + .build(); } private boolean isAnchorTenant(InternetDomainName domainName) { @@ -301,7 +305,7 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { } /** Prohibit creating a domain if there is an open application for the same name. */ - private void verifyNoOpenApplications() throws DomainHasOpenApplicationsException { + private void verifyNoOpenApplications(DateTime now) throws DomainHasOpenApplicationsException { for (DomainApplication application : loadActiveApplicationsByDomainName(targetId, now)) { if (!application.getApplicationStatus().isFinalStatus()) { throw new DomainHasOpenApplicationsException(); @@ -317,7 +321,7 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { } } - private HistoryEntry buildHistory(String repoId, Period period) { + private HistoryEntry buildHistory(String repoId, Period period, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_CREATE) .setPeriod(period) @@ -331,7 +335,8 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { boolean isAnchorTenant, int years, EppCommandOperations commandOperations, - HistoryEntry historyEntry) { + HistoryEntry historyEntry, + DateTime now) { return new BillingEvent.OneTime.Builder() .setReason(Reason.CREATE) .setTargetId(targetId) @@ -387,12 +392,12 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow { .build(); } - private boolean isLrpCreate(Registry registry, boolean isAnchorTenant) { + private boolean isLrpCreate(Registry registry, boolean isAnchorTenant, DateTime now) { return registry.getLrpPeriod().contains(now) && !isAnchorTenant; } private void handleExtraFlowLogic( - String tld, int years, HistoryEntry historyEntry, DomainResource newDomain) + String tld, int years, HistoryEntry historyEntry, DomainResource newDomain, DateTime now) throws EppException { Optional extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForTld(tld); diff --git a/java/google/registry/flows/domain/DomainDeleteFlow.java b/java/google/registry/flows/domain/DomainDeleteFlow.java index 4f60ea191..6dee104e2 100644 --- a/java/google/registry/flows/domain/DomainDeleteFlow.java +++ b/java/google/registry/flows/domain/DomainDeleteFlow.java @@ -40,9 +40,10 @@ import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.EppException.AssociationProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; +import google.registry.flows.SessionMetadata; import google.registry.flows.TransactionalFlow; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; @@ -61,7 +62,9 @@ import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppcommon.Trid; +import google.registry.model.eppinput.EppInput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage.OneTime; @@ -84,7 +87,7 @@ import org.joda.time.DateTime; * @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainDeleteFlow extends Flow implements TransactionalFlow { +public final class DomainDeleteFlow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.LINKED, @@ -93,23 +96,29 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { StatusValue.SERVER_DELETE_PROHIBITED); @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; + @Inject SessionMetadata sessionMetadata; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; @Inject DnsQueue dnsQueue; + @Inject Trid trid; + @Inject EppResponse.Builder responseBuilder; @Inject DomainDeleteFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class, SecDnsCreateExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); // Loads the target resource if it exists DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); Registry registry = Registry.get(existingDomain.getTld()); - verifyDeleteAllowed(existingDomain, registry); - HistoryEntry historyEntry = buildHistoryEntry(existingDomain); + verifyDeleteAllowed(existingDomain, registry, now); + HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now); Builder builder = (Builder) prepareDeletedResourceAsBuilder(existingDomain, now); // If the domain is in the Add Grace Period, we delete it immediately, which is already // reflected in the builder we just prepared. Otherwise we give it a PENDING_DELETE status. @@ -131,7 +140,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { clientId))) .setDeletePollMessage(Key.create(deletePollMessage)); } - handleExtraFlowLogic(existingDomain, historyEntry); + handleExtraFlowLogic(existingDomain, historyEntry, now); DomainResource newDomain = builder.build(); updateForeignKeyIndexDeletionTime(newDomain); handlePendingTransferOnDelete(existingDomain, newDomain, now, historyEntry); @@ -150,13 +159,14 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { } } ofy().save().entities(newDomain, historyEntry); - return createOutput( - newDomain.getDeletionTime().isAfter(now) ? SUCCESS_WITH_ACTION_PENDING : SUCCESS, - null, - getResponseExtensions(existingDomain)); + return responseBuilder + .setResultFromCode( + newDomain.getDeletionTime().isAfter(now) ? SUCCESS_WITH_ACTION_PENDING : SUCCESS) + .setExtensions(getResponseExtensions(existingDomain, now)) + .build(); } - private void verifyDeleteAllowed(DomainResource existingDomain, Registry registry) + private void verifyDeleteAllowed(DomainResource existingDomain, Registry registry, DateTime now) throws EppException { verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES); verifyOptionalAuthInfoForResource(authInfo, existingDomain); @@ -170,7 +180,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { } } - private HistoryEntry buildHistoryEntry(DomainResource existingResource) { + private HistoryEntry buildHistoryEntry(DomainResource existingResource, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_DELETE) .setModificationTime(now) @@ -191,8 +201,9 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { .build(); } - private void handleExtraFlowLogic(DomainResource existingResource, HistoryEntry historyEntry) - throws EppException { + private void handleExtraFlowLogic( + DomainResource existingResource, HistoryEntry historyEntry, DateTime now) + throws EppException { Optional extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource); if (extraFlowLogic.isPresent()) { @@ -203,7 +214,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { @Nullable private ImmutableList getResponseExtensions( - DomainResource existingDomain) { + DomainResource existingDomain, DateTime now) { FeeTransformResponseExtension.Builder feeResponseBuilder = getDeleteResponseBuilder(); if (feeResponseBuilder == null) { return null; @@ -211,7 +222,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { ImmutableList.Builder creditsBuilder = new ImmutableList.Builder<>(); for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) { if (gracePeriod.hasBillingEvent()) { - Money cost = getGracePeriodCost(gracePeriod); + Money cost = getGracePeriodCost(gracePeriod, now); creditsBuilder.add(Credit.create( cost.negated().getAmount(), FeeType.CREDIT, gracePeriod.getType().getXmlName())); feeResponseBuilder.setCurrency(checkNotNull(cost.getCurrencyUnit())); @@ -224,7 +235,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow { return ImmutableList.of(feeResponseBuilder.setCredits(credits).build()); } - private Money getGracePeriodCost(GracePeriod gracePeriod) { + private Money getGracePeriodCost(GracePeriod gracePeriod, DateTime now) { if (gracePeriod.getType() == GracePeriodStatus.AUTO_RENEW) { DateTime autoRenewTime = ofy().load().key(checkNotNull(gracePeriod.getRecurringBillingEvent())).now() diff --git a/java/google/registry/flows/domain/DomainInfoFlow.java b/java/google/registry/flows/domain/DomainInfoFlow.java index f04b6b6b5..a2493e5c1 100644 --- a/java/google/registry/flows/domain/DomainInfoFlow.java +++ b/java/google/registry/flows/domain/DomainInfoFlow.java @@ -19,7 +19,6 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.util.CollectionUtils.forceEmptyToNull; import com.google.common.base.Optional; @@ -41,11 +40,14 @@ import google.registry.model.domain.flags.FlagsInfoResponseExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.RgpInfoExtension; import google.registry.model.eppcommon.AuthInfo; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse.ResponseExtension; +import google.registry.util.Clock; import java.util.Set; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that returns information about a domain. @@ -61,26 +63,30 @@ import javax.inject.Inject; * @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException} * @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException} */ -public final class DomainInfoFlow extends Flow { +public final class DomainInfoFlow implements Flow { @Inject ExtensionManager extensionManager; + @Inject ResourceCommand resourceCommand; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; - @Inject ResourceCommand resourceCommand; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject DomainInfoFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(FeeInfoCommandExtensionV06.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = clock.nowUtc(); DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, domain); - return createOutput( - SUCCESS, - getResourceInfo(domain), - getDomainResponseExtensions(domain)); + return responseBuilder + .setResData(getResourceInfo(domain)) + .setExtensions(getDomainResponseExtensions(domain, now)) + .build(); } private DomainResource getResourceInfo(DomainResource domain) { @@ -111,8 +117,8 @@ public final class DomainInfoFlow extends Flow { return info.build(); } - private ImmutableList getDomainResponseExtensions(DomainResource domain) - throws EppException { + private ImmutableList getDomainResponseExtensions( + DomainResource domain, DateTime now) throws EppException { ImmutableList.Builder extensions = new ImmutableList.Builder<>(); addSecDnsExtensionIfPresent(extensions, domain.getDsData()); ImmutableSet gracePeriodStatuses = domain.getGracePeriodStatuses(); diff --git a/java/google/registry/flows/domain/DomainRenewFlow.java b/java/google/registry/flows/domain/DomainRenewFlow.java index 79496ec5b..a83ae8a82 100644 --- a/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/java/google/registry/flows/domain/DomainRenewFlow.java @@ -28,7 +28,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import static google.registry.model.domain.fee.Fee.FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.DateTimeUtils.leapSafeAddYears; @@ -40,8 +39,8 @@ import google.registry.flows.EppException; import google.registry.flows.EppException.ObjectPendingTransferException; import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; @@ -60,8 +59,9 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; @@ -97,7 +97,7 @@ import org.joda.time.DateTime; * @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException} * @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException} */ -public final class DomainRenewFlow extends Flow implements TransactionalFlow { +public final class DomainRenewFlow implements TransactionalFlow { private static final ImmutableSet RENEW_DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.CLIENT_RENEW_PROHIBITED, @@ -106,18 +106,22 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainRenewFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.registerAsGroup(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); Renew command = (Renew) resourceCommand; // Loads the target resource if it exists DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); @@ -143,7 +147,7 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow { String tld = existingDomain.getTld(); // Bill for this explicit renew itself. BillingEvent.OneTime explicitRenewEvent = - createRenewBillingEvent(tld, commandOperations.getTotalCost(), years, historyEntry); + createRenewBillingEvent(tld, commandOperations.getTotalCost(), years, historyEntry, now); // Create a new autorenew billing event and poll message starting at the new expiration time. BillingEvent.Recurring newAutorenewEvent = newAutorenewBillingEvent(existingDomain) .setEventTime(newExpirationTime) @@ -170,10 +174,10 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow { .build(); ofy().save().entities( newDomain, historyEntry, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage); - return createOutput( - SUCCESS, - DomainRenewData.create(targetId, newExpirationTime), - createResponseExtensions(commandOperations.getTotalCost(), feeRenew)); + return responseBuilder + .setResData(DomainRenewData.create(targetId, newExpirationTime)) + .setExtensions(createResponseExtensions(commandOperations.getTotalCost(), feeRenew)) + .build(); } private void verifyRenewAllowed( @@ -199,7 +203,7 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow { } private OneTime createRenewBillingEvent( - String tld, Money renewCost, int years, HistoryEntry historyEntry) { + String tld, Money renewCost, int years, HistoryEntry historyEntry, DateTime now) { return new BillingEvent.OneTime.Builder() .setReason(Reason.RENEW) .setTargetId(targetId) diff --git a/java/google/registry/flows/domain/DomainRestoreRequestFlow.java b/java/google/registry/flows/domain/DomainRestoreRequestFlow.java index f85838f06..c2f60e313 100644 --- a/java/google/registry/flows/domain/DomainRestoreRequestFlow.java +++ b/java/google/registry/flows/domain/DomainRestoreRequestFlow.java @@ -26,7 +26,6 @@ import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge; import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved; import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; import static google.registry.model.domain.fee.Fee.FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.DateTimeUtils.END_OF_TIME; @@ -40,8 +39,8 @@ import google.registry.flows.EppException; import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; @@ -60,8 +59,9 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.RgpUpdateExtension; import google.registry.model.eppcommon.AuthInfo; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; @@ -103,37 +103,40 @@ import org.joda.time.DateTime; * @error {@link DomainRestoreRequestFlow.DomainNotEligibleForRestoreException} * @error {@link DomainRestoreRequestFlow.RestoreCommandIncludesChangesException} */ -public final class DomainRestoreRequestFlow extends Flow implements TransactionalFlow { +public final class DomainRestoreRequestFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; @Inject DnsQueue dnsQueue; + @Inject EppResponse.Builder responseBuilder; @Inject DomainRestoreRequestFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class, RgpUpdateExtension.class); extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); Update command = (Update) resourceCommand; + DateTime now = ofy().getTransactionTime(); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); Money restoreCost = Registry.get(existingDomain.getTld()).getStandardRestoreCost(); EppCommandOperations renewCommandOperations = TldSpecificLogicProxy.getRenewPrice( Registry.get(existingDomain.getTld()), targetId, clientId, now, 1, eppInput); FeeTransformCommandExtension feeUpdate = eppInput.getFirstExtensionOfClasses( FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); - verifyRestoreAllowed( - command, existingDomain, restoreCost, renewCommandOperations.getTotalCost(), feeUpdate); - HistoryEntry historyEntry = buildHistory(existingDomain); + Money totalCost = renewCommandOperations.getTotalCost(); + verifyRestoreAllowed(command, existingDomain, restoreCost, totalCost, feeUpdate, now); + HistoryEntry historyEntry = buildHistory(existingDomain, now); ImmutableSet.Builder entitiesToSave = new ImmutableSet.Builder<>(); entitiesToSave.addAll( - createRestoreAndRenewBillingEvents( - historyEntry, restoreCost, renewCommandOperations.getTotalCost())); + createRestoreAndRenewBillingEvents(historyEntry, restoreCost, totalCost, now)); // We don't preserve the original expiration time of the domain when we restore, since doing so // would require us to know if they received a grace period refund when they deleted the domain, // and to charge them for that again. Instead, we just say that all restores get a fresh year of @@ -163,13 +166,12 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona ofy().save().entities(entitiesToSave.build()); ofy().delete().key(existingDomain.getDeletePollMessage()); dnsQueue.addDomainRefreshTask(existingDomain.getFullyQualifiedDomainName()); - return createOutput( - SUCCESS, - null, - createResponseExtensions(restoreCost, renewCommandOperations.getTotalCost(), feeUpdate)); + return responseBuilder + .setExtensions(createResponseExtensions(restoreCost, totalCost, feeUpdate)) + .build(); } - private HistoryEntry buildHistory(DomainResource existingDomain) { + private HistoryEntry buildHistory(DomainResource existingDomain, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_RESTORE) .setModificationTime(now) @@ -182,7 +184,8 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona DomainResource existingDomain, Money restoreCost, Money renewCost, - FeeTransformCommandExtension feeUpdate) throws EppException { + FeeTransformCommandExtension feeUpdate, + DateTime now) throws EppException { verifyOptionalAuthInfoForResource(authInfo, existingDomain); if (!isSuperuser) { verifyResourceOwnership(clientId, existingDomain); @@ -202,14 +205,14 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona } private ImmutableSet createRestoreAndRenewBillingEvents( - HistoryEntry historyEntry, Money restoreCost, Money renewCost) { + HistoryEntry historyEntry, Money restoreCost, Money renewCost, DateTime now) { // Bill for the restore. - BillingEvent.OneTime restoreEvent = createRestoreBillingEvent(historyEntry, restoreCost); + BillingEvent.OneTime restoreEvent = createRestoreBillingEvent(historyEntry, restoreCost, now); // Create a new autorenew billing event and poll message starting at the new expiration time. // Also bill for the 1 year cost of a domain renew. This is to avoid registrants being able to // game the system for premium names by renewing, deleting, and then restoring to get a free // year. Note that this billing event has no grace period; it is effective immediately. - BillingEvent.OneTime renewEvent = createRenewBillingEvent(historyEntry, renewCost); + BillingEvent.OneTime renewEvent = createRenewBillingEvent(historyEntry, renewCost, now); return ImmutableSet.of(restoreEvent, renewEvent); } @@ -229,21 +232,22 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona .build(); } - private OneTime createRenewBillingEvent(HistoryEntry historyEntry, Money renewCost) { - return prepareBillingEvent(historyEntry, renewCost) + private OneTime createRenewBillingEvent( + HistoryEntry historyEntry, Money renewCost, DateTime now) { + return prepareBillingEvent(historyEntry, renewCost, now) .setPeriodYears(1) .setReason(Reason.RENEW) .build(); } private BillingEvent.OneTime createRestoreBillingEvent( - HistoryEntry historyEntry, Money restoreCost) { - return prepareBillingEvent(historyEntry, restoreCost) + HistoryEntry historyEntry, Money restoreCost, DateTime now) { + return prepareBillingEvent(historyEntry, restoreCost, now) .setReason(Reason.RESTORE) .build(); } - private Builder prepareBillingEvent(HistoryEntry historyEntry, Money cost) { + private Builder prepareBillingEvent(HistoryEntry historyEntry, Money cost, DateTime now) { return new BillingEvent.OneTime.Builder() .setTargetId(targetId) .setClientId(clientId) diff --git a/java/google/registry/flows/domain/DomainTransferApproveFlow.java b/java/google/registry/flows/domain/DomainTransferApproveFlow.java index 02c909169..7119fdb7d 100644 --- a/java/google/registry/flows/domain/DomainTransferApproveFlow.java +++ b/java/google/registry/flows/domain/DomainTransferApproveFlow.java @@ -27,7 +27,6 @@ import static google.registry.flows.domain.DomainFlowUtils.createGainingTransfer import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost; import static google.registry.util.DateTimeUtils.END_OF_TIME; @@ -38,7 +37,6 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -51,7 +49,7 @@ import google.registry.model.domain.GracePeriod; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; @@ -78,13 +76,14 @@ import org.joda.time.DateTime; * @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainTransferApproveFlow extends Flow implements TransactionalFlow { +public final class DomainTransferApproveFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainTransferApproveFlow() {} /** @@ -92,10 +91,11 @@ public final class DomainTransferApproveFlow extends Flow implements Transaction * {@link DomainResource#cloneProjectedAtTime} which handles implicit server approvals. */ @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyHasPendingTransfer(existingDomain); @@ -187,9 +187,9 @@ public final class DomainTransferApproveFlow extends Flow implements Transaction // Delete the billing event and poll messages that were written in case the transfer would have // been implicitly server approved. ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities()); - return createOutput( - SUCCESS, - createTransferResponse( - targetId, newDomain.getTransferData(), newDomain.getRegistrationExpirationTime())); + return responseBuilder + .setResData(createTransferResponse( + targetId, newDomain.getTransferData(), newDomain.getRegistrationExpirationTime())) + .build(); } } diff --git a/java/google/registry/flows/domain/DomainTransferCancelFlow.java b/java/google/registry/flows/domain/DomainTransferCancelFlow.java index 25dfc837c..5d5d2ba41 100644 --- a/java/google/registry/flows/domain/DomainTransferCancelFlow.java +++ b/java/google/registry/flows/domain/DomainTransferCancelFlow.java @@ -24,7 +24,6 @@ import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToT import static google.registry.flows.domain.DomainFlowUtils.createLosingTransferPollMessage; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.DateTimeUtils.END_OF_TIME; @@ -32,7 +31,6 @@ import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -40,10 +38,11 @@ import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that cancels a pending transfer on a domain. @@ -63,20 +62,22 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NotTransferInitiatorException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainTransferCancelFlow extends Flow implements TransactionalFlow { +public final class DomainTransferCancelFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainTransferCancelFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyHasPendingTransfer(existingDomain); @@ -100,8 +101,8 @@ public final class DomainTransferCancelFlow extends Flow implements Transactiona // Delete the billing event and poll messages that were written in case the transfer would have // been implicitly server approved. ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities()); - return createOutput( - SUCCESS, - createTransferResponse(targetId, newDomain.getTransferData(), null)); + return responseBuilder + .setResData(createTransferResponse(targetId, newDomain.getTransferData(), null)) + .build(); } } diff --git a/java/google/registry/flows/domain/DomainTransferQueryFlow.java b/java/google/registry/flows/domain/DomainTransferQueryFlow.java index 975bef0b6..eec2ca1f9 100644 --- a/java/google/registry/flows/domain/DomainTransferQueryFlow.java +++ b/java/google/registry/flows/domain/DomainTransferQueryFlow.java @@ -19,7 +19,6 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; @@ -31,9 +30,10 @@ import google.registry.flows.exceptions.NoTransferHistoryToQueryException; import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; import google.registry.model.domain.DomainResource; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; +import google.registry.util.Clock; import javax.inject.Inject; import org.joda.time.DateTime; @@ -52,18 +52,21 @@ import org.joda.time.DateTime; * @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException} * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} */ -public final class DomainTransferQueryFlow extends Flow { +public final class DomainTransferQueryFlow implements Flow { @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject DomainTransferQueryFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); + DateTime now = clock.nowUtc(); DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, domain); // Most of the fields on the transfer response are required, so there's no way to return valid @@ -88,6 +91,8 @@ public final class DomainTransferQueryFlow extends Flow { domain.getRegistrationExpirationTime(), transferData.getExtendedRegistrationYears()); } - return createOutput(SUCCESS, createTransferResponse(targetId, transferData, newExpirationTime)); + return responseBuilder + .setResData(createTransferResponse(targetId, transferData, newExpirationTime)) + .build(); } } diff --git a/java/google/registry/flows/domain/DomainTransferRejectFlow.java b/java/google/registry/flows/domain/DomainTransferRejectFlow.java index b2cdbdc77..94f03c11e 100644 --- a/java/google/registry/flows/domain/DomainTransferRejectFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRejectFlow.java @@ -24,7 +24,6 @@ import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToT import static google.registry.flows.domain.DomainFlowUtils.createGainingTransferPollMessage; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.DateTimeUtils.END_OF_TIME; @@ -32,7 +31,6 @@ import com.google.common.base.Optional; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -40,10 +38,11 @@ import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that rejects a pending transfer on a domain. @@ -63,20 +62,22 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} */ -public final class DomainTransferRejectFlow extends Flow implements TransactionalFlow { +public final class DomainTransferRejectFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; + @Inject EppResponse.Builder responseBuilder; @Inject DomainTransferRejectFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); HistoryEntry historyEntry = historyBuilder .setType(HistoryEntry.Type.DOMAIN_TRANSFER_REJECT) @@ -100,8 +101,8 @@ public final class DomainTransferRejectFlow extends Flow implements Transactiona // Delete the billing event and poll messages that were written in case the transfer would have // been implicitly server approved. ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities()); - return createOutput( - SUCCESS, - createTransferResponse(targetId, newDomain.getTransferData(), null)); + return responseBuilder + .setResData(createTransferResponse(targetId, newDomain.getTransferData(), null)) + .build(); } } diff --git a/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 89d7bc804..953691ddf 100644 --- a/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -41,8 +41,8 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AlreadyPendingTransferException; @@ -61,8 +61,10 @@ import google.registry.model.domain.flags.FlagsTransferCommandExtension; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppcommon.Trid; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; @@ -106,7 +108,7 @@ import org.joda.time.Duration; * @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} */ -public final class DomainTransferRequestFlow extends Flow implements TransactionalFlow { +public final class DomainTransferRequestFlow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.CLIENT_TRANSFER_PROHIBITED, @@ -115,22 +117,27 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String gainingClientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; + @Inject Trid trid; + @Inject EppResponse.Builder responseBuilder; @Inject DomainTransferRequestFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(FlagsTransferCommandExtension.class, MetadataExtension.class); extensionManager.registerAsGroup(FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(gainingClientId); Period period = ((Transfer) resourceCommand).getPeriod(); int years = period.getValue(); + DateTime now = ofy().getTransactionTime(); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); - verifyTransferAllowed(existingDomain, period); + verifyTransferAllowed(existingDomain, period, now); String tld = existingDomain.getTld(); Registry registry = Registry.get(tld); // The cost of the renewal implied by a transfer. @@ -139,7 +146,7 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction FeeTransformCommandExtension feeTransfer = eppInput.getFirstExtensionOfClasses( FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); validateFeeChallenge(targetId, tld, now, feeTransfer, renewCost); - HistoryEntry historyEntry = buildHistory(period, existingDomain); + HistoryEntry historyEntry = buildHistory(period, existingDomain, now); DateTime automaticTransferTime = now.plus(registry.getAutomaticTransferLength()); // The new expiration time if there is a server approval. DateTime serverApproveNewExpirationTime = extendRegistrationWithCap( @@ -152,10 +159,11 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction historyEntry, existingDomain, renewCost, - years); + years, + now); // Create the transfer data that represents the pending transfer. TransferData pendingTransferData = createPendingTransferData( - createTransferDataBuilder(existingDomain, automaticTransferTime, years), + createTransferDataBuilder(existingDomain, automaticTransferTime, years, now), serverApproveEntities); // Create a poll message to notify the losing registrar that a transfer was requested. PollMessage requestPollMessage = createLosingTransferPollMessage( @@ -166,7 +174,7 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction // cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones // that we've created in this flow and stored in pendingTransferData. updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime); - handleExtraFlowLogic(years, existingDomain, historyEntry); + handleExtraFlowLogic(years, existingDomain, historyEntry, now); DomainResource newDomain = existingDomain.asBuilder() .setTransferData(pendingTransferData) .addStatusValue(StatusValue.PENDING_TRANSFER) @@ -177,13 +185,14 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction .addAll(serverApproveEntities) .build()) .now(); - return createOutput( - SUCCESS_WITH_ACTION_PENDING, - createResponse(period, existingDomain, newDomain), - createResponseExtensions(renewCost, feeTransfer)); + return responseBuilder + .setResultFromCode(SUCCESS_WITH_ACTION_PENDING) + .setResData(createResponse(period, existingDomain, newDomain, now)) + .setExtensions(createResponseExtensions(renewCost, feeTransfer)) + .build(); } - private void verifyTransferAllowed(DomainResource existingDomain, Period period) + private void verifyTransferAllowed(DomainResource existingDomain, Period period, DateTime now) throws EppException { verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES); verifyRequiredAuthInfoForResourceTransfer(authInfo, existingDomain); @@ -202,7 +211,7 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction } } - private HistoryEntry buildHistory(Period period, DomainResource existingResource) { + private HistoryEntry buildHistory(Period period, DomainResource existingResource, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST) .setPeriod(period) @@ -231,10 +240,11 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction HistoryEntry historyEntry, DomainResource existingDomain, Money renewCost, - int years) { + int years, + DateTime now) { // Create a TransferData for the server-approve case to use for the speculative poll messages. TransferData serverApproveTransferData = - createTransferDataBuilder(existingDomain, automaticTransferTime, years) + createTransferDataBuilder(existingDomain, automaticTransferTime, years, now) .setTransferStatus(TransferStatus.SERVER_APPROVED) .build(); Registry registry = Registry.get(existingDomain.getTld()); @@ -333,9 +343,7 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction } private Builder createTransferDataBuilder( - DomainResource existingDomain, - DateTime automaticTransferTime, - int years) { + DomainResource existingDomain, DateTime automaticTransferTime, int years, DateTime now) { return new TransferData.Builder() .setTransferRequestTrid(trid) .setTransferRequestTime(now) @@ -366,17 +374,23 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction } private void handleExtraFlowLogic( - int years, DomainResource existingDomain, HistoryEntry historyEntry) throws EppException { + int years, DomainResource existingDomain, HistoryEntry historyEntry, DateTime now) + throws EppException { Optional extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain); if (extraFlowLogic.isPresent()) { extraFlowLogic.get().performAdditionalDomainTransferLogic( - existingDomain, gainingClientId, now, years, eppInput, historyEntry); + existingDomain, + gainingClientId, + now, + years, + eppInput, + historyEntry); } } private DomainTransferResponse createResponse( - Period period, DomainResource existingDomain, DomainResource newDomain) { + Period period, DomainResource existingDomain, DomainResource newDomain, DateTime now) { // If the registration were approved this instant, this is what the new expiration would be, // because we cap at 10 years from the moment of approval. This is different than the server // approval new expiration time, which is capped at 10 years from the server approve time. diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java index 30ec444bb..fa865f4e4 100644 --- a/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -38,7 +38,6 @@ import static google.registry.flows.domain.DomainFlowUtils.validateRequiredConta import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; import static google.registry.model.domain.fee.Fee.FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.DateTimeUtils.earliestOf; @@ -49,8 +48,8 @@ import com.googlecode.objectify.Key; import google.registry.dns.DnsQueue; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeUpdateException; @@ -70,8 +69,9 @@ import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; @@ -121,7 +121,7 @@ import org.joda.time.DateTime; * @error {@link DomainFlowUtils.TooManyNameserversException} * @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException} */ -public final class DomainUpdateFlow extends Flow implements TransactionalFlow { +public final class DomainUpdateFlow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -134,15 +134,18 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; @Inject DnsQueue dnsQueue; + @Inject EppResponse.Builder responseBuilder; @Inject DomainUpdateFlow() {} @Override - public EppOutput run() throws EppException { + public EppResponse run() throws EppException { extensionManager.register( FlagsUpdateCommandExtension.class, MetadataExtension.class, @@ -150,37 +153,38 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); Update command = cloneAndLinkReferences((Update) resourceCommand, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); - verifyUpdateAllowed(command, existingDomain); - HistoryEntry historyEntry = buildHistoryEntry(existingDomain); - DomainResource newDomain = performUpdate(command, existingDomain); + verifyUpdateAllowed(command, existingDomain, now); + HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now); + DomainResource newDomain = performUpdate(command, existingDomain, now); // If the new domain is in the sunrush add grace period and is now publishable to DNS because we // have added nameserver or removed holds, we have to convert it to a standard add grace period. if (newDomain.shouldPublishToDns()) { for (GracePeriod gracePeriod : newDomain.getGracePeriods()) { if (gracePeriod.isSunrushAddGracePeriod()) { - newDomain = convertSunrushAddToAdd(newDomain, gracePeriod, historyEntry); + newDomain = convertSunrushAddToAdd(newDomain, gracePeriod, historyEntry, now); break; // There can only be one sunrush add grace period. } } } validateNewState(newDomain); dnsQueue.addDomainRefreshTask(targetId); - handleExtraFlowLogic(existingDomain, historyEntry); + handleExtraFlowLogic(existingDomain, historyEntry, now); ImmutableList.Builder entitiesToSave = new ImmutableList.Builder<>(); entitiesToSave.add(newDomain, historyEntry); Optional statusUpdateBillingEvent = - createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry); + createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry, now); if (statusUpdateBillingEvent.isPresent()) { entitiesToSave.add(statusUpdateBillingEvent.get()); } ofy().save().entities(entitiesToSave.build()); - return createOutput(SUCCESS); + return responseBuilder.build(); } /** Fail if the object doesn't exist or was deleted. */ - private void verifyUpdateAllowed(Update command, DomainResource existingDomain) + private void verifyUpdateAllowed(Update command, DomainResource existingDomain, DateTime now) throws EppException { verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES); verifyOptionalAuthInfoForResource(authInfo, existingDomain); @@ -219,7 +223,7 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { tld, add.getNameserverFullyQualifiedHostNames()); } - private HistoryEntry buildHistoryEntry(DomainResource existingDomain) { + private HistoryEntry buildHistoryEntry(DomainResource existingDomain, DateTime now) { return historyBuilder .setType(HistoryEntry.Type.DOMAIN_UPDATE) .setModificationTime(now) @@ -227,7 +231,7 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { .build(); } - private DomainResource performUpdate(Update command, DomainResource domain) + private DomainResource performUpdate(Update command, DomainResource domain, DateTime now) throws EppException { AddRemove add = command.getInnerAdd(); AddRemove remove = command.getInnerRemove(); @@ -255,12 +259,12 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { } private DomainResource convertSunrushAddToAdd( - DomainResource newDomain, GracePeriod gracePeriod, HistoryEntry historyEntry) { + DomainResource newDomain, GracePeriod gracePeriod, HistoryEntry historyEntry, DateTime now) { // Cancel the billing event for the sunrush add and replace it with a new billing event. BillingEvent.Cancellation billingEventCancellation = BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId); BillingEvent.OneTime billingEvent = - createBillingEventForSunrushConversion(newDomain, historyEntry, gracePeriod); + createBillingEventForSunrushConversion(newDomain, historyEntry, gracePeriod, now); ofy().save().entities(billingEvent, billingEventCancellation); // Modify the grace periods on the domain. return newDomain.asBuilder() @@ -270,7 +274,10 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { } private BillingEvent.OneTime createBillingEventForSunrushConversion( - DomainResource existingDomain, HistoryEntry historyEntry, GracePeriod sunrushAddGracePeriod) { + DomainResource existingDomain, + HistoryEntry historyEntry, + GracePeriod sunrushAddGracePeriod, + DateTime now) { // Compute the expiration time of the add grace period. We will not allow it to be after the // sunrush add grace period expiration time (i.e. you can't get extra add grace period by // setting a nameserver). @@ -304,7 +311,10 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { /** Some status updates cost money. Bill only once no matter how many of them are changed. */ private Optional createBillingEventForStatusUpdates( - DomainResource existingDomain, DomainResource newDomain, HistoryEntry historyEntry) { + DomainResource existingDomain, + DomainResource newDomain, + HistoryEntry historyEntry, + DateTime now) { MetadataExtension metadataExtension = eppInput.getSingleExtension(MetadataExtension.class); if (metadataExtension != null && metadataExtension.getRequestedByRegistrar()) { for (StatusValue statusValue @@ -326,8 +336,8 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow { return Optional.absent(); } - private void handleExtraFlowLogic(DomainResource existingDomain, HistoryEntry historyEntry) - throws EppException { + private void handleExtraFlowLogic( + DomainResource existingDomain, HistoryEntry historyEntry, DateTime now) throws EppException { Optional extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain); if (extraFlowLogic.isPresent()) { diff --git a/java/google/registry/flows/host/HostCheckFlow.java b/java/google/registry/flows/host/HostCheckFlow.java index d26e0c6fc..f520ea8a8 100644 --- a/java/google/registry/flows/host/HostCheckFlow.java +++ b/java/google/registry/flows/host/HostCheckFlow.java @@ -17,7 +17,6 @@ package google.registry.flows.host; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.model.EppResourceUtils.checkResourcesExist; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.collect.ImmutableList; import google.registry.config.ConfigModule.Config; @@ -28,9 +27,10 @@ import google.registry.flows.FlowModule.ClientId; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CheckData.HostCheck; import google.registry.model.eppoutput.CheckData.HostCheckData; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.HostCommand.Check; import google.registry.model.host.HostResource; +import google.registry.util.Clock; import java.util.List; import java.util.Set; import javax.inject.Inject; @@ -42,26 +42,28 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} */ -public final class HostCheckFlow extends Flow { +public final class HostCheckFlow implements Flow { @Inject ResourceCommand resourceCommand; @Inject @ClientId String clientId; @Inject ExtensionManager extensionManager; @Inject @Config("maxChecks") int maxChecks; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject HostCheckFlow() {} @Override - protected final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); List targetIds = ((Check) resourceCommand).getTargetIds(); verifyTargetIdCount(targetIds, maxChecks); - Set existingIds = checkResourcesExist(HostResource.class, targetIds, now); + Set existingIds = checkResourcesExist(HostResource.class, targetIds, clock.nowUtc()); ImmutableList.Builder checks = new ImmutableList.Builder<>(); for (String id : targetIds) { boolean unused = !existingIds.contains(id); checks.add(HostCheck.create(unused, id, unused ? null : "In use")); } - return createOutput(SUCCESS, HostCheckData.create(checks.build())); + return responseBuilder.setResData(HostCheckData.create(checks.build())).build(); } } diff --git a/java/google/registry/flows/host/HostCreateFlow.java b/java/google/registry/flows/host/HostCreateFlow.java index 683e177ca..4736c4149 100644 --- a/java/google/registry/flows/host/HostCreateFlow.java +++ b/java/google/registry/flows/host/HostCreateFlow.java @@ -20,7 +20,6 @@ import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain import static google.registry.flows.host.HostFlowUtils.validateHostName; import static google.registry.flows.host.HostFlowUtils.verifyDomainIsSameRegistrar; import static google.registry.model.EppResourceUtils.createContactHostRoid; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.union; @@ -33,7 +32,6 @@ import google.registry.flows.EppException; import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; @@ -42,7 +40,7 @@ import google.registry.model.domain.DomainResource; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppoutput.CreateData.HostCreateData; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.HostCommand.Create; import google.registry.model.host.HostResource; import google.registry.model.host.HostResource.Builder; @@ -51,6 +49,7 @@ import google.registry.model.index.ForeignKeyIndex; import google.registry.model.ofy.ObjectifyService; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that creates a new host. @@ -70,7 +69,7 @@ import javax.inject.Inject; * @error {@link SubordinateHostMustHaveIpException} * @error {@link UnexpectedExternalHostIpException} */ -public final class HostCreateFlow extends Flow implements TransactionalFlow { +public final class HostCreateFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; @@ -78,14 +77,16 @@ public final class HostCreateFlow extends Flow implements TransactionalFlow { @Inject @TargetId String targetId; @Inject HistoryEntry.Builder historyBuilder; @Inject DnsQueue dnsQueue; + @Inject EppResponse.Builder responseBuilder; @Inject HostCreateFlow() {} @Override - protected final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); Create command = (Create) resourceCommand; + DateTime now = ofy().getTransactionTime(); verifyResourceDoesNotExist(HostResource.class, targetId, now); // The superordinate domain of the host object if creating an in-bailiwick host, or null if // creating an external host. This is looked up before we actually create the Host object so @@ -130,7 +131,7 @@ public final class HostCreateFlow extends Flow implements TransactionalFlow { dnsQueue.addHostRefreshTask(targetId); } ofy().save().entities(entitiesToSave); - return createOutput(SUCCESS, HostCreateData.create(targetId, now)); + return responseBuilder.setResData(HostCreateData.create(targetId, now)).build(); } /** Subordinate hosts must have an ip address. */ diff --git a/java/google/registry/flows/host/HostDeleteFlow.java b/java/google/registry/flows/host/HostDeleteFlow.java index e6f1a21d3..e5cb23358 100644 --- a/java/google/registry/flows/host/HostDeleteFlow.java +++ b/java/google/registry/flows/host/HostDeleteFlow.java @@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowEnqueuer; @@ -38,10 +38,11 @@ import google.registry.model.domain.DomainBase; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.HostResource; import google.registry.model.reporting.HistoryEntry; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that deletes a host. @@ -57,7 +58,7 @@ import javax.inject.Inject; * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} * @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} */ -public final class HostDeleteFlow extends Flow implements TransactionalFlow { +public final class HostDeleteFlow implements TransactionalFlow { private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( StatusValue.LINKED, @@ -76,15 +77,18 @@ public final class HostDeleteFlow extends Flow implements TransactionalFlow { @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; + @Inject EppResponse.Builder responseBuilder; @Inject HostDeleteFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); + DateTime now = ofy().getTransactionTime(); failfastForAsyncDelete(targetId, now, HostResource.class, GET_NAMESERVERS); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES); @@ -100,6 +104,6 @@ public final class HostDeleteFlow extends Flow implements TransactionalFlow { .setModificationTime(now) .setParent(Key.create(existingHost)); ofy().save().entities(newHost, historyBuilder.build()); - return createOutput(SUCCESS_WITH_ACTION_PENDING); + return responseBuilder.setResultFromCode(SUCCESS_WITH_ACTION_PENDING).build(); } } diff --git a/java/google/registry/flows/host/HostInfoFlow.java b/java/google/registry/flows/host/HostInfoFlow.java index aedc4c094..bf1eb577b 100644 --- a/java/google/registry/flows/host/HostInfoFlow.java +++ b/java/google/registry/flows/host/HostInfoFlow.java @@ -18,7 +18,6 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import com.google.common.base.Optional; import google.registry.flows.EppException; @@ -27,9 +26,11 @@ import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.model.eppcommon.AuthInfo; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.HostResource; +import google.registry.util.Clock; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that returns information about a host. @@ -39,20 +40,23 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} */ -public final class HostInfoFlow extends Flow { +public final class HostInfoFlow implements Flow { @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @TargetId String targetId; @Inject Optional authInfo; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject HostInfoFlow() {} @Override - public EppOutput run() throws EppException { + public EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); + DateTime now = clock.nowUtc(); HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now); verifyOptionalAuthInfoForResource(authInfo, host); - return createOutput(SUCCESS, cloneResourceWithLinkedStatus(host, now)); + return responseBuilder.setResData(cloneResourceWithLinkedStatus(host, now)).build(); } } diff --git a/java/google/registry/flows/host/HostUpdateFlow.java b/java/google/registry/flows/host/HostUpdateFlow.java index b54ce71ae..c3fa071d5 100644 --- a/java/google/registry/flows/host/HostUpdateFlow.java +++ b/java/google/registry/flows/host/HostUpdateFlow.java @@ -26,7 +26,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain; import static google.registry.flows.host.HostFlowUtils.validateHostName; import static google.registry.flows.host.HostFlowUtils.verifyDomainIsSameRegistrar; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.CollectionUtils.isNullOrEmpty; @@ -41,8 +40,8 @@ import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.async.AsyncFlowEnqueuer; @@ -53,7 +52,7 @@ import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.HostCommand.Update; import google.registry.model.host.HostCommand.Update.AddRemove; import google.registry.model.host.HostCommand.Update.Change; @@ -62,6 +61,7 @@ import google.registry.model.index.ForeignKeyIndex; import google.registry.model.reporting.HistoryEntry; import java.util.Objects; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow that updates a host. @@ -91,7 +91,7 @@ import javax.inject.Inject; * @error {@link RenameHostToExternalRemoveIpException} * @error {@link RenameHostToSubordinateRequiresIpException} */ -public final class HostUpdateFlow extends Flow implements TransactionalFlow { +public final class HostUpdateFlow implements TransactionalFlow { /** * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it @@ -107,19 +107,22 @@ public final class HostUpdateFlow extends Flow implements TransactionalFlow { @Inject Optional authInfo; @Inject @ClientId String clientId; @Inject @TargetId String targetId; + @Inject @Superuser boolean isSuperuser; @Inject HistoryEntry.Builder historyBuilder; @Inject AsyncFlowEnqueuer asyncFlowEnqueuer; @Inject DnsQueue dnsQueue; + @Inject EppResponse.Builder responseBuilder; @Inject HostUpdateFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); extensionManager.validate(); validateClientIsLoggedIn(clientId); Update command = (Update) resourceCommand; Change change = command.getInnerChange(); String suppliedNewHostName = change.getFullyQualifiedHostName(); + DateTime now = ofy().getTransactionTime(); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); boolean isHostRename = suppliedNewHostName != null; String oldHostName = targetId; @@ -169,7 +172,7 @@ public final class HostUpdateFlow extends Flow implements TransactionalFlow { .setParent(Key.create(existingHost)) .build()); ofy().save().entities(entitiesToSave.build()); - return createOutput(SUCCESS); + return responseBuilder.build(); } private void verifyUpdateAllowed( diff --git a/java/google/registry/flows/picker/FlowPicker.java b/java/google/registry/flows/picker/FlowPicker.java index 5fa1b70fe..c5693c066 100644 --- a/java/google/registry/flows/picker/FlowPicker.java +++ b/java/google/registry/flows/picker/FlowPicker.java @@ -91,7 +91,7 @@ import java.util.Set; public class FlowPicker { /** Marker class for unimplemented flows. */ - private abstract static class UnimplementedFlow extends Flow {} + private abstract static class UnimplementedFlow implements Flow {} /** A function type that takes an {@link EppInput} and returns a {@link Flow} class. */ private abstract static class FlowProvider { diff --git a/java/google/registry/flows/poll/PollAckFlow.java b/java/google/registry/flows/poll/PollAckFlow.java index 22bd3e042..161433cfb 100644 --- a/java/google/registry/flows/poll/PollAckFlow.java +++ b/java/google/registry/flows/poll/PollAckFlow.java @@ -17,7 +17,6 @@ package google.registry.flows.poll; import static com.google.common.base.Preconditions.checkState; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery; -import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.DateTimeUtils.isBeforeOrAt; @@ -30,11 +29,10 @@ import google.registry.flows.EppException.ObjectDoesNotExistException; import google.registry.flows.EppException.ParameterValueSyntaxErrorException; import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.ExtensionManager; -import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.PollMessageId; import google.registry.flows.TransactionalFlow; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.MessageQueueInfo; import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessageExternalKeyConverter; @@ -55,15 +53,16 @@ import org.joda.time.DateTime; * @error {@link PollAckFlow.MissingMessageIdException} * @error {@link PollAckFlow.NotAuthorizedToAckMessageException} */ -public class PollAckFlow extends Flow implements TransactionalFlow { +public class PollAckFlow implements TransactionalFlow { @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @PollMessageId String messageId; + @Inject EppResponse.Builder responseBuilder; @Inject PollAckFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); if (messageId.isEmpty()) { @@ -78,6 +77,8 @@ public class PollAckFlow extends Flow implements TransactionalFlow { throw new InvalidMessageIdException(messageId); } + final DateTime now = ofy().getTransactionTime(); + // 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. PollMessage pollMessage = ofy().load().key(pollMessageKey).now(); @@ -128,17 +129,14 @@ public class PollAckFlow extends Flow implements TransactionalFlow { messageCount--; } if (messageCount <= 0) { - return createOutput(SUCCESS_WITH_NO_MESSAGES); + return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build(); } - return createOutput( - SUCCESS, - null, // responseData - null, // responseExtensions - MessageQueueInfo.create( - null, // eventTime - null, // msg - messageCount, - messageId)); + return responseBuilder + .setMessageQueueInfo(new MessageQueueInfo.Builder() + .setQueueLength(messageCount) + .setMessageId(messageId) + .build()) + .build(); } /** Registrar is not authorized to ack this message. */ diff --git a/java/google/registry/flows/poll/PollRequestFlow.java b/java/google/registry/flows/poll/PollRequestFlow.java index 74d2e2699..822b20e38 100644 --- a/java/google/registry/flows/poll/PollRequestFlow.java +++ b/java/google/registry/flows/poll/PollRequestFlow.java @@ -27,11 +27,13 @@ import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.PollMessageId; -import google.registry.model.eppoutput.EppOutput; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.MessageQueueInfo; import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessageExternalKeyConverter; +import google.registry.util.Clock; import javax.inject.Inject; +import org.joda.time.DateTime; /** * An EPP flow for requesting {@link PollMessage}s. @@ -44,34 +46,39 @@ import javax.inject.Inject; * * @error {@link PollRequestFlow.UnexpectedMessageIdException} */ -public class PollRequestFlow extends Flow { +public class PollRequestFlow implements Flow { @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; @Inject @PollMessageId String messageId; + @Inject Clock clock; + @Inject EppResponse.Builder responseBuilder; @Inject PollRequestFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); if (!messageId.isEmpty()) { throw new UnexpectedMessageIdException(); } // Return the oldest message from the queue. + DateTime now = clock.nowUtc(); PollMessage pollMessage = getPollMessagesQuery(clientId, now).first().now(); if (pollMessage == null) { - return createOutput(SUCCESS_WITH_NO_MESSAGES); + return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build(); } - return createOutput( - SUCCESS_WITH_ACK_MESSAGE, - forceEmptyToNull(pollMessage.getResponseData()), - forceEmptyToNull(pollMessage.getResponseExtensions()), - MessageQueueInfo.create( - pollMessage.getEventTime(), - pollMessage.getMsg(), - getPollMessagesQuery(clientId, now).count(), - PollMessage.EXTERNAL_KEY_CONVERTER.convert(Key.create(pollMessage)))); + return responseBuilder + .setResultFromCode(SUCCESS_WITH_ACK_MESSAGE) + .setMessageQueueInfo(new MessageQueueInfo.Builder() + .setQueueDate(pollMessage.getEventTime()) + .setMsg(pollMessage.getMsg()) + .setQueueLength(getPollMessagesQuery(clientId, now).count()) + .setMessageId(PollMessage.EXTERNAL_KEY_CONVERTER.convert(Key.create(pollMessage))) + .build()) + .setMultipleResData(forceEmptyToNull(pollMessage.getResponseData())) + .setExtensions(forceEmptyToNull(pollMessage.getResponseExtensions())) + .build(); } /** Unexpected message id. */ diff --git a/java/google/registry/flows/session/HelloFlow.java b/java/google/registry/flows/session/HelloFlow.java index e6ae67e01..78662ffd8 100644 --- a/java/google/registry/flows/session/HelloFlow.java +++ b/java/google/registry/flows/session/HelloFlow.java @@ -17,19 +17,20 @@ package google.registry.flows.session; import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; -import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.Greeting; +import google.registry.util.Clock; import javax.inject.Inject; /** A flow for an Epp "hello". */ -public class HelloFlow extends Flow { +public class HelloFlow implements Flow { @Inject ExtensionManager extensionManager; + @Inject Clock clock; @Inject HelloFlow() {} @Override - public EppOutput run() throws EppException { + public Greeting run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. - return EppOutput.create(Greeting.create(now)); + return Greeting.create(clock.nowUtc()); } } diff --git a/java/google/registry/flows/session/LoginFlow.java b/java/google/registry/flows/session/LoginFlow.java index 6cdd17f4d..86a9a5c28 100644 --- a/java/google/registry/flows/session/LoginFlow.java +++ b/java/google/registry/flows/session/LoginFlow.java @@ -30,13 +30,15 @@ import google.registry.flows.EppException.UnimplementedOptionException; import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; +import google.registry.flows.SessionMetadata; +import google.registry.flows.TransportCredentials; import google.registry.model.eppcommon.ProtocolDefinition; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; +import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput.Login; import google.registry.model.eppinput.EppInput.Options; import google.registry.model.eppinput.EppInput.Services; -import google.registry.model.eppoutput.EppOutput; -import google.registry.model.eppoutput.Result.Code; +import google.registry.model.eppoutput.EppResponse; import google.registry.model.registrar.Registrar; import google.registry.util.FormattingLogger; import java.util.Set; @@ -62,7 +64,7 @@ import javax.inject.Inject; * @error {@link LoginFlow.RegistrarAccountNotActiveException} * @error {@link LoginFlow.UnsupportedLanguageException} */ -public class LoginFlow extends Flow { +public class LoginFlow implements Flow { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); @@ -70,12 +72,16 @@ public class LoginFlow extends Flow { private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3; @Inject ExtensionManager extensionManager; + @Inject EppInput eppInput; + @Inject SessionMetadata sessionMetadata; + @Inject TransportCredentials credentials; @Inject @ClientId String clientId; + @Inject EppResponse.Builder responseBuilder; @Inject LoginFlow() {} /** Run the flow and log errors. */ @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { try { return runWithoutLogging(); } catch (EppException e) { @@ -85,7 +91,7 @@ public class LoginFlow extends Flow { } /** Run the flow without bothering to log errors. The {@link #run} method will do that for us. */ - public final EppOutput runWithoutLogging() throws EppException { + public final EppResponse runWithoutLogging() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. Login login = (Login) eppInput.getCommandWrapper().getCommand(); if (!clientId.isEmpty()) { @@ -137,7 +143,7 @@ public class LoginFlow extends Flow { sessionMetadata.resetFailedLoginAttempts(); sessionMetadata.setClientId(login.getClientId()); sessionMetadata.setServiceExtensionUris(serviceExtensionUrisBuilder.build()); - return createOutput(Code.SUCCESS); + return responseBuilder.build(); } /** Registrar with this client ID could not be found. */ diff --git a/java/google/registry/flows/session/LogoutFlow.java b/java/google/registry/flows/session/LogoutFlow.java index bb38188fb..4cb622311 100644 --- a/java/google/registry/flows/session/LogoutFlow.java +++ b/java/google/registry/flows/session/LogoutFlow.java @@ -21,7 +21,8 @@ import google.registry.flows.EppException; import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; -import google.registry.model.eppoutput.EppOutput; +import google.registry.flows.SessionMetadata; +import google.registry.model.eppoutput.EppResponse; import javax.inject.Inject; /** @@ -29,17 +30,19 @@ import javax.inject.Inject; * * @error {@link google.registry.flows.FlowUtils.NotLoggedInException} */ -public class LogoutFlow extends Flow { +public class LogoutFlow implements Flow { @Inject ExtensionManager extensionManager; @Inject @ClientId String clientId; + @Inject SessionMetadata sessionMetadata; + @Inject EppResponse.Builder responseBuilder; @Inject LogoutFlow() {} @Override - public final EppOutput run() throws EppException { + public final EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. validateClientIsLoggedIn(clientId); sessionMetadata.invalidate(); - return createOutput(SUCCESS_AND_CLOSE); + return responseBuilder.setResultFromCode(SUCCESS_AND_CLOSE).build(); } } diff --git a/java/google/registry/model/eppoutput/EppResponse.java b/java/google/registry/model/eppoutput/EppResponse.java index a678f1954..9ed84c4a6 100644 --- a/java/google/registry/model/eppoutput/EppResponse.java +++ b/java/google/registry/model/eppoutput/EppResponse.java @@ -197,6 +197,10 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting { return this; } + public Builder setResultFromCode(Result.Code resultCode) { + return setResult(Result.create(resultCode)); + } + public Builder setResult(Result result) { getInstance().result = result; return this; @@ -207,11 +211,19 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting { return this; } - public Builder setResData(@Nullable ImmutableList resData) { + public Builder setResData(ResponseData onlyResData) { + return setMultipleResData(ImmutableList.of(onlyResData)); + } + + public Builder setMultipleResData(@Nullable ImmutableList resData) { getInstance().resData = resData; return this; } + public Builder setOnlyExtension(ResponseExtension onlyExtension) { + return setExtensions(ImmutableList.of(onlyExtension)); + } + public Builder setExtensions(@Nullable ImmutableList extensions) { getInstance().extensions = extensions; return this; diff --git a/java/google/registry/model/poll/MessageQueueInfo.java b/java/google/registry/model/poll/MessageQueueInfo.java index 06d7bf5c9..5103a96da 100644 --- a/java/google/registry/model/poll/MessageQueueInfo.java +++ b/java/google/registry/model/poll/MessageQueueInfo.java @@ -14,10 +14,11 @@ package google.registry.model.poll; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; -import javax.annotation.Nullable; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import org.joda.time.DateTime; @@ -40,32 +41,34 @@ public class MessageQueueInfo extends ImmutableObject { @XmlAttribute(name = "id") String messageId; - public DateTime getQueueDate() { - return queueDate; - } + /** A builder for constructing a {@link MessageQueueInfo}, since it's immutable. */ + public static class Builder extends Buildable.Builder { + public Builder setQueueDate(DateTime queueDate) { + getInstance().queueDate = queueDate; + return this; + } - public String getMsg() { - return msg; - } + public Builder setMsg(String msg) { + getInstance().msg = msg; + return this; + } - public Integer getQueueLength() { - return queueLength; - } + public Builder setQueueLength(int queueLength) { + checkArgument(queueLength >= 0); + getInstance().queueLength = queueLength; + return this; + } - public String getMessageId() { - return messageId; - } + public Builder setMessageId(String messageId) { + getInstance().messageId = messageId; + return this; + } - public static MessageQueueInfo create( - @Nullable DateTime queueDate, - @Nullable String msg, - Integer queueLength, - String messageId) { - MessageQueueInfo instance = new MessageQueueInfo(); - instance.queueDate = queueDate; - instance.msg = msg; - instance.queueLength = checkNotNull(queueLength); - instance.messageId = checkNotNull(messageId); - return instance; + @Override + public MessageQueueInfo build() { + checkNotNull(getInstance().messageId); + checkNotNull(getInstance().queueLength); + return super.build(); + } } } diff --git a/javatests/google/registry/flows/FlowRunnerTest.java b/javatests/google/registry/flows/FlowRunnerTest.java index 25b7ea00d..787d14717 100644 --- a/javatests/google/registry/flows/FlowRunnerTest.java +++ b/javatests/google/registry/flows/FlowRunnerTest.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions; import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import com.google.appengine.api.users.User; import com.google.common.base.Joiner; @@ -31,8 +30,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.testing.TestLogHandler; import google.registry.model.eppcommon.Trid; -import google.registry.model.eppinput.EppInput; -import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse; import google.registry.monitoring.whitebox.EppMetric; import google.registry.testing.AppEngineRule; @@ -66,27 +63,23 @@ public class FlowRunnerTest extends ShardableTestCase { public void before() { Logger.getLogger(FlowRunner.class.getCanonicalName()).addHandler(handler); - final EppOutput eppOutput = mock(EppOutput.class); - EppResponse eppResponse = mock(EppResponse.class); - when(eppOutput.getResponse()).thenReturn(eppResponse); + final EppResponse eppResponse = mock(EppResponse.class); flowRunner.clientId = "TheRegistrar"; - flowRunner.clock = new FakeClock(); flowRunner.credentials = new PasswordOnlyTransportCredentials(); - flowRunner.eppInput = new EppInput(); flowRunner.eppRequestSource = EppRequestSource.UNIT_TEST; flowRunner.flowProvider = Providers.of( new Flow() { @Override - protected EppOutput run() { - return eppOutput; + public EppResponse run() { + return eppResponse; }}); flowRunner.inputXmlBytes = "".getBytes(UTF_8); flowRunner.isDryRun = false; flowRunner.isSuperuser = false; flowRunner.isTransactional = false; - flowRunner.metric = EppMetric.builderForRequest("request-id-1", flowRunner.clock); + flowRunner.metric = EppMetric.builderForRequest("request-id-1", new FakeClock()); flowRunner.sessionMetadata = new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.of()); flowRunner.trid = Trid.create("client-123", "server-456"); diff --git a/javatests/google/registry/model/domain/DomainResourceTest.java b/javatests/google/registry/model/domain/DomainResourceTest.java index 3d7bfd4bd..89d3d8549 100644 --- a/javatests/google/registry/model/domain/DomainResourceTest.java +++ b/javatests/google/registry/model/domain/DomainResourceTest.java @@ -52,7 +52,6 @@ import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.Trid; import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse; -import google.registry.model.eppoutput.Result; import google.registry.model.eppoutput.Result.Code; import google.registry.model.host.HostResource; import google.registry.model.ofy.RequestCapturingAsyncDatastoreService; @@ -443,8 +442,8 @@ public class DomainResourceTest extends EntityTestCase { int numPreviousReads = RequestCapturingAsyncDatastoreService.getReads().size(); EppXmlTransformer.marshal( EppOutput.create(new EppResponse.Builder() - .setResult(Result.create(Code.SUCCESS)) - .setResData(ImmutableList.of(domain)) + .setResultFromCode(Code.SUCCESS) + .setResData(domain) .setTrid(Trid.create(null, "abc")) .build()), ValidationMode.STRICT); diff --git a/javatests/google/registry/model/poll/MessageQueueInfoTest.java b/javatests/google/registry/model/poll/MessageQueueInfoTest.java deleted file mode 100644 index 5dd2a4397..000000000 --- a/javatests/google/registry/model/poll/MessageQueueInfoTest.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2016 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.model.poll; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.testing.NullPointerTester; -import com.google.common.testing.NullPointerTester.Visibility; -import org.joda.time.DateTime; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link MessageQueueInfo}. */ -@RunWith(JUnit4.class) -public final class MessageQueueInfoTest { - - @Test - public void testDeadCodeWeDontWantToDelete() throws Exception { - MessageQueueInfo mp = new MessageQueueInfo(); - mp.queueDate = DateTime.parse("1984-12-18TZ"); - assertThat(mp.getQueueDate()).isEqualTo(DateTime.parse("1984-12-18TZ")); - mp.msg = "sloth"; - assertThat(mp.getMsg()).isEqualTo("sloth"); - mp.queueLength = 123; - assertThat(mp.getQueueLength()).isEqualTo(123); - mp.messageId = "adorable"; - assertThat(mp.getMessageId()).isEqualTo("adorable"); - } - - @Test - public void testNullness() { - NullPointerTester tester = new NullPointerTester(); - tester.testStaticMethods(MessageQueueInfo.class, Visibility.PROTECTED); - } -} diff --git a/javatests/google/registry/model/translators/StatusValueAdapterTest.java b/javatests/google/registry/model/translators/StatusValueAdapterTest.java index 03aba0ebc..3a9685a50 100644 --- a/javatests/google/registry/model/translators/StatusValueAdapterTest.java +++ b/javatests/google/registry/model/translators/StatusValueAdapterTest.java @@ -17,7 +17,6 @@ package google.registry.model.translators; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import google.registry.flows.EppXmlTransformer; import google.registry.model.eppcommon.StatusValue; @@ -51,9 +50,9 @@ public class StatusValueAdapterTest { String marshalled = new String( EppXmlTransformer.marshal( EppOutput.create(new EppResponse.Builder() - .setResData(ImmutableList.of(new HostResource.Builder() + .setResData(new HostResource.Builder() .addStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED) - .build())) + .build()) .build()), ValidationMode.LENIENT), UTF_8);