Turn Flow into an interface and inject all its fields

This concludes your flow flattening experience. Please
fill out a flow flattening satisfaction survey before
exiting.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=137903095
This commit is contained in:
cgoldfeder 2016-11-01 19:50:31 -07:00 committed by Ben McIlwain
parent 82b0bff9b5
commit 053538b1b5
49 changed files with 630 additions and 569 deletions

View file

@ -14,90 +14,24 @@
package google.registry.flows; package google.registry.flows;
import com.google.common.collect.ImmutableList; import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting;
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;
/** /**
* An abstract EPP flow. * An Extensible Provisioning Protocol flow.
* *
* <p>This class also contains static methods for loading an appropriate flow based on model * <p>A "flow" is our word for a "command", since we've overloaded the word "command" to mean the
* classes. * command objects and also the CLI operation classes.
*/ */
public abstract class Flow { public interface 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<? extends ResponseExtension> extensions) {
return createOutput(
code, responseData == null ? null : ImmutableList.of(responseData), extensions, null);
}
protected EppOutput createOutput(
Result.Code code,
@Nullable ImmutableList<ResponseData> responseData,
@Nullable ImmutableList<? extends ResponseExtension> responseExtensions,
@Nullable MessageQueueInfo messageQueueInfo) {
return EppOutput.create(new EppResponse.Builder()
.setTrid(trid)
.setResult(Result.create(code))
.setMessageQueueInfo(messageQueueInfo)
.setResData(responseData)
.setExtensions(responseExtensions)
.build());
}
/** /**
* Using an init function instead of a constructor avoids duplicating constructors across the * Executes an EPP "flow" and returns a response object (or in the specific case of the "hello"
* entire hierarchy of flow classes * flow a greeting object) that can be converted to XML and returned to the caller.
*
* <p>Flows should have {@link #run} called once per instance. If a flow needs to be retried, a
* new instance should be created.
*
* <p>Flows should get all of their parameters via injection off of {@link FlowComponent}.
*/ */
public final Flow init( ResponseOrGreeting run() throws EppException;
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;
}
} }

View file

@ -31,6 +31,8 @@ import google.registry.model.eppinput.EppInput.Poll;
import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; 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 google.registry.model.reporting.HistoryEntry;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import javax.inject.Qualifier; import javax.inject.Qualifier;
@ -244,6 +246,19 @@ public class FlowModule {
return historyBuilder; return historyBuilder;
} }
/**
* Provides a partially filled in {@link EppResponse} builder.
*
* <p>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. */ /** Wrapper class to carry an {@link EppException} to the calling code. */
static class EppExceptionInProviderException extends RuntimeException { static class EppExceptionInProviderException extends RuntimeException {
EppExceptionInProviderException(EppException exception) { EppExceptionInProviderException(EppException exception) {

View file

@ -28,14 +28,11 @@ import google.registry.flows.FlowModule.InputXml;
import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.Transactional; import google.registry.flows.FlowModule.Transactional;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
import google.registry.monitoring.whitebox.EppMetric; import google.registry.monitoring.whitebox.EppMetric;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import org.joda.time.DateTime;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
/** Run a flow, either transactionally or not, with logging and retrying as needed. */ /** 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(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject Clock clock;
@Inject TransportCredentials credentials; @Inject TransportCredentials credentials;
@Inject EppInput eppInput;
@Inject EppRequestSource eppRequestSource; @Inject EppRequestSource eppRequestSource;
@Inject Provider<Flow> flowProvider; @Inject Provider<Flow> flowProvider;
@Inject @InputXml byte[] inputXmlBytes; @Inject @InputXml byte[] inputXmlBytes;
@ -99,7 +94,7 @@ public class FlowRunner {
"xmlBytes", xmlBase64))); "xmlBytes", xmlBase64)));
if (!isTransactional) { if (!isTransactional) {
metric.incrementAttempts(); metric.incrementAttempts();
return createAndInitFlow(clock.nowUtc()).run(); return EppOutput.create(flowProvider.get().run());
} }
try { try {
return ofy().transact(new Work<EppOutput>() { return ofy().transact(new Work<EppOutput>() {
@ -107,7 +102,7 @@ public class FlowRunner {
public EppOutput run() { public EppOutput run() {
metric.incrementAttempts(); metric.incrementAttempts();
try { try {
EppOutput output = createAndInitFlow(ofy().getTransactionTime()).run(); EppOutput output = EppOutput.create(flowProvider.get().run());
if (isDryRun) { if (isDryRun) {
throw new DryRunException(output); 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. */ /** Exception for canceling a transaction while capturing what the output would have been. */
private static class DryRunException extends RuntimeException { private static class DryRunException extends RuntimeException {
final EppOutput output; final EppOutput output;

View file

@ -15,9 +15,9 @@
package google.registry.flows; 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.
* *
* <p>Any flow that mutates the datastore should be tagged with this so that {@link FlowRunner} will * <p>Any flow that mutates the datastore should implement this so that {@link FlowRunner} will know
* know how to run it. * how to run it.
*/ */
public interface TransactionalFlow {} public interface TransactionalFlow extends Flow {}

View file

@ -17,7 +17,6 @@ package google.registry.flows.contact;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.checkResourcesExist;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import google.registry.config.ConfigModule.Config; 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.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CheckData.ContactCheck; import google.registry.model.eppoutput.CheckData.ContactCheck;
import google.registry.model.eppoutput.CheckData.ContactCheckData; 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.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
@ -42,26 +42,28 @@ import javax.inject.Inject;
* *
* @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException}
*/ */
public final class ContactCheckFlow extends Flow { public final class ContactCheckFlow implements Flow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Clock clock;
@Inject @Config("maxChecks") int maxChecks; @Inject @Config("maxChecks") int maxChecks;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactCheckFlow() {} @Inject ContactCheckFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); List<String> targetIds = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(targetIds, maxChecks);
Set<String> existingIds = checkResourcesExist(ContactResource.class, targetIds, now); Set<String> existingIds = checkResourcesExist(ContactResource.class, targetIds, clock.nowUtc());
ImmutableList.Builder<ContactCheck> checks = new ImmutableList.Builder<>(); ImmutableList.Builder<ContactCheck> checks = new ImmutableList.Builder<>();
for (String id : targetIds) { for (String id : targetIds) {
boolean unused = !existingIds.contains(id); boolean unused = !existingIds.contains(id);
checks.add(ContactCheck.create(unused, id, unused ? null : "In use")); 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();
} }
} }

View file

@ -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.validateAsciiPostalInfo;
import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy;
import static google.registry.model.EppResourceUtils.createContactHostRoid; 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.model.ofy.ObjectifyService.ofy;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.ContactCreateData; 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.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.ofy.ObjectifyService; import google.registry.model.ofy.ObjectifyService;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that creates a new contact. * An EPP flow that creates a new contact.
@ -49,21 +48,23 @@ import javax.inject.Inject;
* @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException}
* @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException}
*/ */
public final class ContactCreateFlow extends Flow implements TransactionalFlow { public final class ContactCreateFlow implements TransactionalFlow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactCreateFlow() {} @Inject ContactCreateFlow() {}
@Override @Override
protected final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
Create command = (Create) resourceCommand; Create command = (Create) resourceCommand;
DateTime now = ofy().getTransactionTime();
verifyResourceDoesNotExist(ContactResource.class, targetId, now); verifyResourceDoesNotExist(ContactResource.class, targetId, now);
ContactResource newContact = new Builder() ContactResource newContact = new Builder()
.setContactId(targetId) .setContactId(targetId)
@ -90,6 +91,8 @@ public final class ContactCreateFlow extends Flow implements TransactionalFlow {
historyBuilder.build(), historyBuilder.build(),
ForeignKeyIndex.create(newContact, newContact.getDeletionTime()), ForeignKeyIndex.create(newContact, newContact.getDeletionTime()),
EppResourceIndex.create(Key.create(newContact))); EppResourceIndex.create(Key.create(newContact)));
return createOutput(SUCCESS, ContactCreateData.create(newContact.getContactId(), now)); return responseBuilder
.setResData(ContactCreateData.create(newContact.getContactId(), now))
.build();
} }
} }

View file

@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.async.AsyncFlowEnqueuer; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that deletes a contact. * 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.ResourceStatusProhibitsOperationException}
* @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} * @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<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.LINKED, StatusValue.LINKED,
@ -75,16 +76,19 @@ public final class ContactDeleteFlow extends Flow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject AsyncFlowEnqueuer asyncFlowEnqueuer; @Inject AsyncFlowEnqueuer asyncFlowEnqueuer;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactDeleteFlow() {} @Inject ContactDeleteFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
failfastForAsyncDelete(targetId, now, ContactResource.class, GET_REFERENCED_CONTACTS); failfastForAsyncDelete(targetId, now, ContactResource.class, GET_REFERENCED_CONTACTS);
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
verifyNoDisallowedStatuses(existingContact, DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingContact, DISALLOWED_STATUSES);
@ -100,6 +104,6 @@ public final class ContactDeleteFlow extends Flow implements TransactionalFlow {
.setModificationTime(now) .setModificationTime(now)
.setParent(Key.create(existingContact)); .setParent(Key.create(existingContact));
ofy().save().<Object>entities(newContact, historyBuilder.build()); ofy().save().<Object>entities(newContact, historyBuilder.build());
return createOutput(SUCCESS_WITH_ACTION_PENDING); return responseBuilder.setResultFromCode(SUCCESS_WITH_ACTION_PENDING).build();
} }
} }

View file

@ -18,7 +18,6 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -28,8 +27,10 @@ import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.eppcommon.AuthInfo; 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 javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that returns information about a contact. * An EPP flow that returns information about a contact.
@ -41,16 +42,19 @@ import javax.inject.Inject;
* *
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
*/ */
public final class ContactInfoFlow extends Flow { public final class ContactInfoFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Clock clock;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactInfoFlow() {} @Inject ContactInfoFlow() {}
@Override @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. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now);
@ -58,6 +62,6 @@ public final class ContactInfoFlow extends Flow {
if (!clientId.equals(contact.getCurrentSponsorClientId()) && !authInfo.isPresent()) { if (!clientId.equals(contact.getCurrentSponsorClientId()) && !authInfo.isPresent()) {
contact = contact.asBuilder().setAuthInfo(null).build(); contact = contact.asBuilder().setAuthInfo(null).build();
} }
return createOutput(SUCCESS, cloneResourceWithLinkedStatus(contact, now)); return responseBuilder.setResData(cloneResourceWithLinkedStatus(contact, now)).build();
} }
} }

View file

@ -21,14 +21,12 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage;
import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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 static google.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.ResourceCommand; 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.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that approves a pending transfer on a contact. * 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.ResourceFlowUtils.ResourceDoesNotExistException}
* @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @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 ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@ -65,6 +64,7 @@ public final class ContactTransferApproveFlow extends Flow implements Transactio
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactTransferApproveFlow() {} @Inject ContactTransferApproveFlow() {}
/** /**
@ -72,10 +72,11 @@ public final class ContactTransferApproveFlow extends Flow implements Transactio
* {@link ContactResource#cloneProjectedAtTime} which handles implicit server approvals. * {@link ContactResource#cloneProjectedAtTime} which handles implicit server approvals.
*/ */
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, existingContact); verifyOptionalAuthInfoForResource(authInfo, existingContact);
TransferData transferData = existingContact.getTransferData(); 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 // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
ofy().delete().keys(transferData.getServerApproveEntities()); ofy().delete().keys(transferData.getServerApproveEntities());
return createOutput(SUCCESS, createTransferResponse(targetId, newContact.getTransferData())); return responseBuilder
.setResData(createTransferResponse(targetId, newContact.getTransferData()))
.build();
} }
} }

View file

@ -20,14 +20,12 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.flows.contact.ContactFlowUtils.createLosingTransferPollMessage; import static google.registry.flows.contact.ContactFlowUtils.createLosingTransferPollMessage;
import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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 static google.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.ResourceCommand; 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.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that cancels a pending transfer on a contact. * 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.NotPendingTransferException}
* @error {@link google.registry.flows.exceptions.NotTransferInitiatorException} * @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 ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@ -65,13 +64,15 @@ public final class ContactTransferCancelFlow extends Flow implements Transaction
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactTransferCancelFlow() {} @Inject ContactTransferCancelFlow() {}
@Override @Override
protected final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, existingContact); verifyOptionalAuthInfoForResource(authInfo, existingContact);
TransferData transferData = existingContact.getTransferData(); 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 // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
ofy().delete().keys(transferData.getServerApproveEntities()); ofy().delete().keys(transferData.getServerApproveEntities());
return createOutput(SUCCESS, createTransferResponse(targetId, newContact.getTransferData())); return responseBuilder
.setResData(createTransferResponse(targetId, newContact.getTransferData()))
.build();
} }
} }

View file

@ -18,7 +18,6 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -30,7 +29,8 @@ import google.registry.flows.exceptions.NoTransferHistoryToQueryException;
import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; import google.registry.flows.exceptions.NotAuthorizedToViewTransferException;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.eppcommon.AuthInfo; 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 javax.inject.Inject;
/** /**
@ -48,19 +48,22 @@ import javax.inject.Inject;
* @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException} * @error {@link google.registry.flows.exceptions.NoTransferHistoryToQueryException}
* @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException}
*/ */
public final class ContactTransferQueryFlow extends Flow { public final class ContactTransferQueryFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactTransferQueryFlow() {} @Inject ContactTransferQueryFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource contact =
loadAndVerifyExistence(ContactResource.class, targetId, clock.nowUtc());
verifyOptionalAuthInfoForResource(authInfo, contact); verifyOptionalAuthInfoForResource(authInfo, contact);
// Most of the fields on the transfer response are required, so there's no way to return valid // 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). // 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())) { && !clientId.equals(contact.getTransferData().getLosingClientId())) {
throw new NotAuthorizedToViewTransferException(); throw new NotAuthorizedToViewTransferException();
} }
return createOutput(SUCCESS, createTransferResponse(targetId, contact.getTransferData())); return responseBuilder
.setResData(createTransferResponse(targetId, contact.getTransferData()))
.build();
} }
} }

View file

@ -21,14 +21,12 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage; import static google.registry.flows.contact.ContactFlowUtils.createGainingTransferPollMessage;
import static google.registry.flows.contact.ContactFlowUtils.createTransferResponse; 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 static google.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.contact.ContactResource;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that rejects a pending transfer on a contact. * 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.ResourceFlowUtils.ResourceNotOwnedException}
* @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @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 ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactTransferRejectFlow() {} @Inject ContactTransferRejectFlow() {}
@Override @Override
protected final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, existingContact); verifyOptionalAuthInfoForResource(authInfo, existingContact);
TransferData transferData = existingContact.getTransferData(); 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 // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
ofy().delete().keys(transferData.getServerApproveEntities()); ofy().delete().keys(transferData.getServerApproveEntities());
return createOutput(SUCCESS, createTransferResponse(targetId, newContact.getTransferData())); return responseBuilder
.setResData(createTransferResponse(targetId, newContact.getTransferData()))
.build();
} }
} }

View file

@ -30,7 +30,6 @@ import com.googlecode.objectify.Key;
import google.registry.config.ConfigModule.Config; import google.registry.config.ConfigModule.Config;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData; 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.MissingTransferRequestAuthInfoException}
* @error {@link google.registry.flows.exceptions.ObjectAlreadySponsoredException} * @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<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.CLIENT_TRANSFER_PROHIBITED, StatusValue.CLIENT_TRANSFER_PROHIBITED,
@ -77,13 +77,16 @@ public final class ContactTransferRequestFlow extends Flow implements Transactio
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Config("contactAutomaticTransferLength") Duration automaticTransferLength; @Inject @Config("contactAutomaticTransferLength") Duration automaticTransferLength;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject Trid trid;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactTransferRequestFlow() {} @Inject ContactTransferRequestFlow() {}
@Override @Override
protected final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(gainingClientId); validateClientIsLoggedIn(gainingClientId);
DateTime now = ofy().getTransactionTime();
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
verifyRequiredAuthInfoForResourceTransfer(authInfo, existingContact); verifyRequiredAuthInfoForResourceTransfer(authInfo, existingContact);
// Verify that the resource does not already have a pending transfer. // Verify that the resource does not already have a pending transfer.
@ -138,9 +141,10 @@ public final class ContactTransferRequestFlow extends Flow implements Transactio
requestPollMessage, requestPollMessage,
serverApproveGainingPollMessage, serverApproveGainingPollMessage,
serverApproveLosingPollMessage); serverApproveLosingPollMessage);
return createOutput( return responseBuilder
SUCCESS_WITH_ACTION_PENDING, .setResultFromCode(SUCCESS_WITH_ACTION_PENDING)
createTransferResponse(targetId, newContact.getTransferData())); .setResData(createTransferResponse(targetId, newContact.getTransferData()))
.build();
} }
} }

View file

@ -24,7 +24,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForR
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo; import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo;
import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy; 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 static google.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.base.Optional; import com.google.common.base.Optional;
@ -32,8 +31,8 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException; 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.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.ResourceCommand; 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 google.registry.model.reporting.HistoryEntry;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that updates a contact. * An EPP flow that updates a contact.
@ -63,7 +63,7 @@ import javax.inject.Inject;
* @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException}
* @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} * @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 * 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> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject ContactUpdateFlow() {} @Inject ContactUpdateFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
Update command = (Update) resourceCommand; Update command = (Update) resourceCommand;
DateTime now = ofy().getTransactionTime();
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now); ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, existingContact); verifyOptionalAuthInfoForResource(authInfo, existingContact);
ImmutableSet<StatusValue> statusToRemove = command.getInnerRemove().getStatusValues(); ImmutableSet<StatusValue> statusToRemove = command.getInnerRemove().getStatusValues();
@ -146,7 +149,7 @@ public final class ContactUpdateFlow extends Flow implements TransactionalFlow {
validateAsciiPostalInfo(newContact.getInternationalizedPostalInfo()); validateAsciiPostalInfo(newContact.getInternationalizedPostalInfo());
validateContactAgainstPolicy(newContact); validateContactAgainstPolicy(newContact);
ofy().save().<Object>entities(newContact, historyBuilder.build()); ofy().save().<Object>entities(newContact, historyBuilder.build());
return createOutput(SUCCESS); return responseBuilder.build();
} }
/** Return the first non-null param, or null if both are null. */ /** Return the first non-null param, or null if both are null. */

View file

@ -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.verifyClaimsPeriodNotEnded;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.domain.launch.LaunchPhase.CLAIMS; 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.ImmutableList;
import com.google.common.collect.ImmutableSet; 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.ExtensionManager;
import google.registry.flows.Flow; import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.DomainCommand.Check;
import google.registry.model.domain.launch.LaunchCheckExtension; import google.registry.model.domain.launch.LaunchCheckExtension;
import google.registry.model.domain.launch.LaunchCheckResponseExtension; import google.registry.model.domain.launch.LaunchCheckResponseExtension;
import google.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheck; import google.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheck;
import google.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheckName; import google.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheckName;
import google.registry.model.eppinput.ResourceCommand; 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;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.tmch.ClaimsListShard; import google.registry.model.tmch.ClaimsListShard;
import google.registry.util.Clock;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that checks whether strings are trademarked. * An EPP flow that checks whether strings are trademarked.
@ -58,16 +60,19 @@ import javax.inject.Inject;
* @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link DomainFlowUtils.TldDoesNotExistException}
* @error {@link ClaimsCheckNotAllowedInSunrise} * @error {@link ClaimsCheckNotAllowedInSunrise}
*/ */
public final class ClaimsCheckFlow extends Flow { public final class ClaimsCheckFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @Superuser boolean isSuperuser;
@Inject Clock clock;
@Inject @Config("maxChecks") int maxChecks; @Inject @Config("maxChecks") int maxChecks;
@Inject EppResponse.Builder responseBuilder;
@Inject ClaimsCheckFlow() {} @Inject ClaimsCheckFlow() {}
@Override @Override
public EppOutput run() throws EppException { public EppResponse run() throws EppException {
extensionManager.register(LaunchCheckExtension.class); extensionManager.register(LaunchCheckExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
@ -84,6 +89,7 @@ public final class ClaimsCheckFlow extends Flow {
checkAllowedAccessToTld(clientId, tld); checkAllowedAccessToTld(clientId, tld);
Registry registry = Registry.get(tld); Registry registry = Registry.get(tld);
if (!isSuperuser) { if (!isSuperuser) {
DateTime now = clock.nowUtc();
verifyNotInPredelegation(registry, now); verifyNotInPredelegation(registry, now);
if (registry.getTldState(now) == TldState.SUNRISE) { if (registry.getTldState(now) == TldState.SUNRISE) {
throw new ClaimsCheckNotAllowedInSunrise(); throw new ClaimsCheckNotAllowedInSunrise();
@ -96,10 +102,9 @@ public final class ClaimsCheckFlow extends Flow {
LaunchCheck.create( LaunchCheck.create(
LaunchCheckName.create(claimKey != null, targetId), claimKey)); LaunchCheckName.create(claimKey != null, targetId), claimKey));
} }
return createOutput( return responseBuilder
SUCCESS, .setOnlyExtension(LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build()))
null, .build();
ImmutableList.of(LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build())));
} }
/** Claims checks are not allowed during sunrise. */ /** Claims checks are not allowed during sunrise. */

View file

@ -48,8 +48,8 @@ import google.registry.flows.EppException.AuthorizationErrorException;
import google.registry.flows.EppException.ObjectDoesNotExistException; import google.registry.flows.EppException.ObjectDoesNotExistException;
import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; 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.domain.secdns.SecDnsCreateExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.DomainCreateData; import google.registry.model.eppoutput.CreateData.DomainCreateData;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.eppoutput.Result;
import google.registry.model.index.EppResourceIndex; import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.ofy.ObjectifyService; import google.registry.model.ofy.ObjectifyService;
@ -97,7 +97,7 @@ import org.joda.time.DateTime;
* @error {@link DomainAllocateFlow.MissingApplicationException} * @error {@link DomainAllocateFlow.MissingApplicationException}
* @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException} * @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException}
*/ */
public class DomainAllocateFlow extends Flow implements TransactionalFlow { public class DomainAllocateFlow implements TransactionalFlow {
private static final String COLLISION_MESSAGE = private static final String COLLISION_MESSAGE =
"Domain on the name collision list was allocated. But by policy, the domain will not be " "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 ResourceCommand resourceCommand;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppInput eppInput;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainAllocateFlow() {} @Inject DomainAllocateFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
SecDnsCreateExtension.class, SecDnsCreateExtension.class,
FlagsCreateCommandExtension.class, FlagsCreateCommandExtension.class,
@ -123,6 +126,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
verifyIsSuperuser(); verifyIsSuperuser();
DateTime now = ofy().getTransactionTime();
Create command = cloneAndLinkReferences((Create) resourceCommand, now); Create command = cloneAndLinkReferences((Create) resourceCommand, now);
failfastForCreate(targetId, now); failfastForCreate(targetId, now);
verifyResourceDoesNotExist(DomainResource.class, targetId, now); verifyResourceDoesNotExist(DomainResource.class, targetId, now);
@ -137,13 +141,14 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
boolean isSunrushAddGracePeriod = isNullOrEmpty(command.getNameservers()); boolean isSunrushAddGracePeriod = isNullOrEmpty(command.getNameservers());
AllocateCreateExtension allocateCreate = AllocateCreateExtension allocateCreate =
eppInput.getSingleExtension(AllocateCreateExtension.class); eppInput.getSingleExtension(AllocateCreateExtension.class);
DomainApplication application = loadAndValidateApplication(allocateCreate.getApplicationRoid()); DomainApplication application =
loadAndValidateApplication(allocateCreate.getApplicationRoid(), now);
String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr()); String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr());
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>(); ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
HistoryEntry historyEntry = buildHistory(repoId, period); HistoryEntry historyEntry = buildHistory(repoId, period, now);
entitiesToSave.add(historyEntry); entitiesToSave.add(historyEntry);
ImmutableSet<? extends ImmutableObject> billsAndPolls = createBillingEventsAndPollMessages( ImmutableSet<? extends ImmutableObject> billsAndPolls = createBillingEventsAndPollMessages(
domainName, application, historyEntry, isSunrushAddGracePeriod, registry, years); domainName, application, historyEntry, isSunrushAddGracePeriod, registry, now, years);
entitiesToSave.addAll(billsAndPolls); entitiesToSave.addAll(billsAndPolls);
DateTime registrationExpirationTime = leapSafeAddYears(now, years); DateTime registrationExpirationTime = leapSafeAddYears(now, years);
DomainResource newDomain = new DomainResource.Builder() DomainResource newDomain = new DomainResource.Builder()
@ -173,21 +178,21 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
.build(); .build();
entitiesToSave.add( entitiesToSave.add(
newDomain, newDomain,
buildApplicationHistory(application), buildApplicationHistory(application, now),
updateApplication(application), updateApplication(application),
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()), ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
EppResourceIndex.create(Key.create(newDomain))); EppResourceIndex.create(Key.create(newDomain)));
// Anchor tenant registrations override LRP. // Anchor tenant registrations override LRP.
String authInfoToken = authInfo.getPw().getValue(); String authInfoToken = authInfo.getPw().getValue();
if (hasLrpToken(domainName, registry, authInfoToken)) { if (hasLrpToken(domainName, registry, authInfoToken, now)) {
entitiesToSave.add(prepareMarkedLrpTokenEntity(authInfoToken, domainName, historyEntry)); entitiesToSave.add(prepareMarkedLrpTokenEntity(authInfoToken, domainName, historyEntry));
} }
ofy().save().entities(entitiesToSave.build()); ofy().save().entities(entitiesToSave.build());
enqueueTasks(allocateCreate, newDomain); enqueueTasks(allocateCreate, newDomain);
return createOutput( return responseBuilder
Result.Code.SUCCESS, .setResData(DomainCreateData.create(targetId, now, registrationExpirationTime))
DomainCreateData.create(targetId, now, registrationExpirationTime), .setExtensions(createResponseExtensions(now, registry, years))
createResponseExtensions(registry, years)); .build();
} }
private <T extends ImmutableObject> T getOnly( private <T extends ImmutableObject> 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); DomainApplication application = loadDomainApplication(applicationRoid, now);
if (application == null) { if (application == null) {
throw new MissingApplicationException(applicationRoid); throw new MissingApplicationException(applicationRoid);
@ -212,7 +218,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
return application; return application;
} }
private HistoryEntry buildHistory(String repoId, Period period) { private HistoryEntry buildHistory(String repoId, Period period, DateTime now) {
return historyBuilder return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_ALLOCATE) .setType(HistoryEntry.Type.DOMAIN_ALLOCATE)
.setPeriod(period) .setPeriod(period)
@ -227,12 +233,13 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
HistoryEntry historyEntry, HistoryEntry historyEntry,
boolean isSunrushAddGracePeriod, boolean isSunrushAddGracePeriod,
Registry registry, Registry registry,
DateTime now,
int years) { int years) {
DateTime registrationExpirationTime = leapSafeAddYears(now, years); DateTime registrationExpirationTime = leapSafeAddYears(now, years);
BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent( BillingEvent.OneTime oneTimeBillingEvent = createOneTimeBillingEvent(
application, historyEntry, isSunrushAddGracePeriod, registry, years); application, historyEntry, isSunrushAddGracePeriod, registry, now, years);
PollMessage.OneTime oneTimePollMessage = 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. // Create a new autorenew billing event and poll message starting at the expiration time.
BillingEvent.Recurring autorenewBillingEvent = BillingEvent.Recurring autorenewBillingEvent =
createAutorenewBillingEvent(historyEntry, registrationExpirationTime); createAutorenewBillingEvent(historyEntry, registrationExpirationTime);
@ -250,6 +257,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
HistoryEntry historyEntry, HistoryEntry historyEntry,
boolean isSunrushAddGracePeriod, boolean isSunrushAddGracePeriod,
Registry registry, Registry registry,
DateTime now,
int years) { int years) {
return new BillingEvent.OneTime.Builder() return new BillingEvent.OneTime.Builder()
.setReason(Reason.CREATE) .setReason(Reason.CREATE)
@ -296,7 +304,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
.build(); .build();
} }
private GracePeriod createGracePeriod(boolean isSunrushAddGracePeriod, private static GracePeriod createGracePeriod(boolean isSunrushAddGracePeriod,
BillingEvent.OneTime oneTimeBillingEvent) { BillingEvent.OneTime oneTimeBillingEvent) {
return GracePeriod.forBillingEvent( return GracePeriod.forBillingEvent(
isSunrushAddGracePeriod ? GracePeriodStatus.SUNRUSH_ADD : GracePeriodStatus.ADD, 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. */ /** 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() return new HistoryEntry.Builder()
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE) .setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE)
.setParent(application) .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. */ /** Create a poll message informing the registrar that the application status was updated. */
private PollMessage.OneTime createOneTimePollMessage( private PollMessage.OneTime createOneTimePollMessage(
DomainApplication application, HistoryEntry historyEntry, ReservationType reservationType) { DomainApplication application,
HistoryEntry historyEntry,
ReservationType reservationType,
DateTime now) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setClientId(historyEntry.getClientId()) .setClientId(historyEntry.getClientId())
.setEventTime(now) .setEventTime(now)
@ -345,7 +356,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
} }
private boolean hasLrpToken( private boolean hasLrpToken(
InternetDomainName domainName, Registry registry, String authInfoToken) { InternetDomainName domainName, Registry registry, String authInfoToken, DateTime now) {
return registry.getLrpPeriod().contains(now) return registry.getLrpPeriod().contains(now)
&& !matchesAnchorTenantReservation(domainName, authInfoToken); && !matchesAnchorTenantReservation(domainName, authInfoToken);
} }
@ -360,7 +371,7 @@ public class DomainAllocateFlow extends Flow implements TransactionalFlow {
} }
private ImmutableList<FeeTransformResponseExtension> createResponseExtensions( private ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
Registry registry, int years) throws EppException { DateTime now, Registry registry, int years) throws EppException {
EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice( EppCommandOperations commandOperations = TldSpecificLogicProxy.getCreatePrice(
registry, targetId, clientId, now, years, eppInput); registry, targetId, clientId, now, years, eppInput);
FeeTransformCommandExtension feeCreate = FeeTransformCommandExtension feeCreate =

View file

@ -39,7 +39,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifySignedMarks;
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.model.EppResourceUtils.createDomainRoid; 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.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.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation; 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.ObjectAlreadyExistsException;
import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.RequiredParameterMissingException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; 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.domain.secdns.SecDnsCreateExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.DomainCreateData; 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.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.index.DomainApplicationIndex; import google.registry.model.index.DomainApplicationIndex;
import google.registry.model.index.EppResourceIndex; 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.AbstractSignedMark;
import google.registry.model.smd.EncodedSignedMark; import google.registry.model.smd.EncodedSignedMark;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that creates a new application for a domain resource. * 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.UnsupportedFeeAttributeException}
* @error {@link DomainFlowUtils.UnsupportedMarkTypeException} * @error {@link DomainFlowUtils.UnsupportedMarkTypeException}
*/ */
public final class DomainApplicationCreateFlow extends Flow implements TransactionalFlow { public final class DomainApplicationCreateFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject AuthInfo authInfo; @Inject AuthInfo authInfo;
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject Trid trid;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainApplicationCreateFlow() {} @Inject DomainApplicationCreateFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
SecDnsCreateExtension.class, SecDnsCreateExtension.class,
FlagsCreateCommandExtension.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.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
Create command = cloneAndLinkReferences((Create) resourceCommand, now); Create command = cloneAndLinkReferences((Create) resourceCommand, now);
failfastForCreate(targetId, now); failfastForCreate(targetId, now);
// Fail if the domain is already registered (e.g. this is a landrush application but the domain // 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); validateCreateCommandContactsAndNameservers(command, tld);
LaunchCreateExtension launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class); LaunchCreateExtension launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class);
if (launchCreate != null) { if (launchCreate != null) {
validateLaunchCreateExtension(launchCreate, registry, domainName); validateLaunchCreateExtension(launchCreate, registry, domainName, now);
} }
boolean isAnchorTenant = boolean isAnchorTenant =
matchesAnchorTenantReservation(domainName, authInfo.getPw().getValue()); matchesAnchorTenantReservation(domainName, authInfo.getPw().getValue());
if (!isSuperuser) { if (!isSuperuser) {
verifyPremiumNameIsNotBlocked(targetId, now, clientId); verifyPremiumNameIsNotBlocked(targetId, now, clientId);
prohibitLandrushIfExactlyOneSunrise(registry); prohibitLandrushIfExactlyOneSunrise(registry, now);
if (!isAnchorTenant) { if (!isAnchorTenant) {
boolean isSunriseApplication = !launchCreate.getSignedMarks().isEmpty(); boolean isSunriseApplication = !launchCreate.getSignedMarks().isEmpty();
verifyNotReserved(domainName, isSunriseApplication); verifyNotReserved(domainName, isSunriseApplication);
@ -236,7 +243,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti
}}) }})
.toList()) .toList())
.build(); .build();
HistoryEntry historyEntry = buildHistory(newApplication.getRepoId(), command.getPeriod()); HistoryEntry historyEntry = buildHistory(newApplication.getRepoId(), command.getPeriod(), now);
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>(); ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
handleExtraFlowLogic( handleExtraFlowLogic(
registry.getTldStr(), command.getPeriod().getValue(), historyEntry, newApplication); 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)); prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry));
} }
ofy().save().entities(entitiesToSave.build()); ofy().save().entities(entitiesToSave.build());
return createOutput( return responseBuilder
SUCCESS, .setResData(DomainCreateData.create(targetId, now, null))
DomainCreateData.create(targetId, now, null), .setExtensions(createResponseExtensions(
createResponseExtensions( newApplication.getForeignKey(), launchCreate.getPhase(), feeCreate, commandOperations))
newApplication.getForeignKey(), launchCreate.getPhase(), feeCreate, commandOperations)); .build();
} }
private void validateLaunchCreateExtension( private void validateLaunchCreateExtension(
LaunchCreateExtension launchCreate, Registry registry, InternetDomainName domainName) LaunchCreateExtension launchCreate,
throws EppException { Registry registry,
InternetDomainName domainName,
DateTime now) throws EppException {
verifyNoCodeMarks(launchCreate); verifyNoCodeMarks(launchCreate);
boolean hasClaimsNotice = launchCreate.getNotice() != null; boolean hasClaimsNotice = launchCreate.getNotice() != null;
if (hasClaimsNotice) { 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 * Prohibit creating a landrush application in LANDRUSH (but not in SUNRUSH) if there is exactly
* one sunrise application for the same name. * one sunrise application for the same name.
*/ */
private void prohibitLandrushIfExactlyOneSunrise(Registry registry) private void prohibitLandrushIfExactlyOneSunrise(Registry registry, DateTime now)
throws UncontestedSunriseApplicationBlockedInLandrushException { throws UncontestedSunriseApplicationBlockedInLandrushException {
if (registry.getTldState(now) == TldState.LANDRUSH) { if (registry.getTldState(now) == TldState.LANDRUSH) {
ImmutableSet<DomainApplication> applications = ImmutableSet<DomainApplication> 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 return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_CREATE) .setType(HistoryEntry.Type.DOMAIN_APPLICATION_CREATE)
.setPeriod(period) .setPeriod(period)
@ -349,7 +358,7 @@ public final class DomainApplicationCreateFlow extends Flow implements Transacti
extraFlowLogic.get().performAdditionalApplicationCreateLogic( extraFlowLogic.get().performAdditionalApplicationCreateLogic(
newApplication, newApplication,
clientId, clientId,
now, newApplication.getCreationTime(),
years, years,
eppInput, eppInput,
historyEntry); historyEntry);

View file

@ -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.verifyLaunchPhaseMatchesRegistryPhase;
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsLaunchFlows; import static google.registry.flows.domain.DomainFlowUtils.verifyRegistryStateAllowsLaunchFlows;
import static google.registry.model.EppResourceUtils.loadDomainApplication; 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 static google.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.base.Optional; 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;
import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ApplicationId;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.model.domain.DomainApplication; 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.launch.LaunchPhase;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.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;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that deletes a domain application. * An EPP flow that deletes a domain application.
@ -62,21 +63,25 @@ import javax.inject.Inject;
* @error {@link DomainFlowUtils.LaunchPhaseMismatchException} * @error {@link DomainFlowUtils.LaunchPhaseMismatchException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
*/ */
public final class DomainApplicationDeleteFlow extends Flow implements TransactionalFlow { public final class DomainApplicationDeleteFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @ApplicationId String applicationId; @Inject @ApplicationId String applicationId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainApplicationDeleteFlow() {} @Inject DomainApplicationDeleteFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class, LaunchDeleteExtension.class); extensionManager.register(MetadataExtension.class, LaunchDeleteExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
DomainApplication existingApplication = verifyExistence( DomainApplication existingApplication = verifyExistence(
DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); DomainApplication.class, applicationId, loadDomainApplication(applicationId, now));
verifyApplicationDomainMatchesTargetId(existingApplication, targetId); verifyApplicationDomainMatchesTargetId(existingApplication, targetId);
@ -105,7 +110,7 @@ public final class DomainApplicationDeleteFlow extends Flow implements Transacti
updateForeignKeyIndexDeletionTime(newApplication); updateForeignKeyIndexDeletionTime(newApplication);
handlePendingTransferOnDelete(existingApplication, newApplication, now, historyEntry); handlePendingTransferOnDelete(existingApplication, newApplication, now, historyEntry);
ofy().save().<Object>entities(newApplication, historyEntry); ofy().save().<Object>entities(newApplication, historyEntry);
return createOutput(SUCCESS); return responseBuilder.build();
} }
/** A sunrise application cannot be deleted during landrush. */ /** A sunrise application cannot be deleted during landrush. */

View file

@ -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.addSecDnsExtensionIfPresent;
import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId; import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId;
import static google.registry.model.EppResourceUtils.loadDomainApplication; 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.base.Optional;
import com.google.common.collect.ImmutableList; 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.LaunchInfoExtension;
import google.registry.model.domain.launch.LaunchInfoResponseExtension; import google.registry.model.domain.launch.LaunchInfoResponseExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.mark.Mark; import google.registry.model.mark.Mark;
import google.registry.model.smd.EncodedSignedMark; import google.registry.model.smd.EncodedSignedMark;
import google.registry.model.smd.SignedMark; import google.registry.model.smd.SignedMark;
import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
@ -59,18 +60,21 @@ import javax.inject.Inject;
* @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException} * @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException}
* @error {@link MissingApplicationIdException} * @error {@link MissingApplicationIdException}
*/ */
public final class DomainApplicationInfoFlow extends Flow { public final class DomainApplicationInfoFlow implements Flow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @ApplicationId String applicationId; @Inject @ApplicationId String applicationId;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainApplicationInfoFlow() {} @Inject DomainApplicationInfoFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(LaunchInfoExtension.class); extensionManager.register(LaunchInfoExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
@ -78,7 +82,9 @@ public final class DomainApplicationInfoFlow extends Flow {
throw new MissingApplicationIdException(); throw new MissingApplicationIdException();
} }
DomainApplication application = verifyExistence( DomainApplication application = verifyExistence(
DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); DomainApplication.class,
applicationId,
loadDomainApplication(applicationId, clock.nowUtc()));
verifyApplicationDomainMatchesTargetId(application, targetId); verifyApplicationDomainMatchesTargetId(application, targetId);
verifyOptionalAuthInfoForResource(authInfo, application); verifyOptionalAuthInfoForResource(authInfo, application);
LaunchInfoExtension launchInfo = eppInput.getSingleExtension(LaunchInfoExtension.class); 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. // We don't support authInfo for applications, so if it's another registrar always fail.
verifyResourceOwnership(clientId, application); verifyResourceOwnership(clientId, application);
return createOutput( return responseBuilder
SUCCESS, .setResData(getResourceInfo(application))
getResourceInfo(application), .setExtensions(getDomainResponseExtensions(application, launchInfo))
getDomainResponseExtensions(application, launchInfo)); .build();
} }
DomainApplication getResourceInfo(DomainApplication application) { DomainApplication getResourceInfo(DomainApplication application) {

View file

@ -40,7 +40,6 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNot
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
import static google.registry.model.EppResourceUtils.loadDomainApplication; 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.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.model.ofy.ObjectifyService.ofy;
import com.google.common.base.Optional; 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;
import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ApplicationId; import google.registry.flows.FlowModule.ApplicationId;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.model.ImmutableObject; 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.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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 google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that updates a domain application. * An EPP flow that updates a domain application.
@ -101,7 +102,7 @@ import javax.inject.Inject;
* @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException} * @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException}
* @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException} * @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 * 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 ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @ApplicationId String applicationId; @Inject @ApplicationId String applicationId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainApplicationUpdateFlow() {} @Inject DomainApplicationUpdateFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
LaunchUpdateExtension.class, LaunchUpdateExtension.class,
MetadataExtension.class, MetadataExtension.class,
@ -137,6 +141,7 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl
extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
Update command = cloneAndLinkReferences((Update) resourceCommand, now); Update command = cloneAndLinkReferences((Update) resourceCommand, now);
DomainApplication existingApplication = verifyExistence( DomainApplication existingApplication = verifyExistence(
DomainApplication.class, applicationId, loadDomainApplication(applicationId, now)); DomainApplication.class, applicationId, loadDomainApplication(applicationId, now));
@ -144,11 +149,11 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl
verifyNoDisallowedStatuses(existingApplication, UPDATE_DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingApplication, UPDATE_DISALLOWED_STATUSES);
verifyOptionalAuthInfoForResource(authInfo, existingApplication); verifyOptionalAuthInfoForResource(authInfo, existingApplication);
verifyUpdateAllowed(existingApplication, command); verifyUpdateAllowed(existingApplication, command);
HistoryEntry historyEntry = buildHistory(existingApplication); HistoryEntry historyEntry = buildHistory(existingApplication, now);
DomainApplication newApplication = updateApplication(existingApplication, command); DomainApplication newApplication = updateApplication(existingApplication, command, now);
validateNewApplication(newApplication); validateNewApplication(newApplication);
ofy().save().<ImmutableObject>entities(newApplication, historyEntry); ofy().save().<ImmutableObject>entities(newApplication, historyEntry);
return createOutput(SUCCESS); return responseBuilder.build();
} }
protected final void verifyUpdateAllowed( protected final void verifyUpdateAllowed(
@ -178,7 +183,7 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl
tld, add.getNameserverFullyQualifiedHostNames()); tld, add.getNameserverFullyQualifiedHostNames());
} }
private HistoryEntry buildHistory(DomainApplication existingApplication) { private HistoryEntry buildHistory(DomainApplication existingApplication, DateTime now) {
return historyBuilder return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE) .setType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE)
.setModificationTime(now) .setModificationTime(now)
@ -187,7 +192,7 @@ public class DomainApplicationUpdateFlow extends Flow implements TransactionalFl
} }
private DomainApplication updateApplication( private DomainApplication updateApplication(
DomainApplication application, Update command) throws EppException { DomainApplication application, Update command, DateTime now) throws EppException {
AddRemove add = command.getInnerAdd(); AddRemove add = command.getInnerAdd();
AddRemove remove = command.getInnerRemove(); AddRemove remove = command.getInnerRemove();
checkSameValuesNotAddedAndRemoved(add.getNameservers(), remove.getNameservers()); checkSameValuesNotAddedAndRemoved(add.getNameservers(), remove.getNameservers());

View file

@ -24,12 +24,9 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWit
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.EppResourceUtils.checkResourcesExist; 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_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.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
import static google.registry.model.registry.label.ReservationType.UNRESERVED; import static google.registry.model.registry.label.ReservationType.UNRESERVED;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable; 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.ExtensionManager;
import google.registry.flows.Flow; import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainApplication;
import google.registry.model.domain.DomainCommand.Check; import google.registry.model.domain.DomainCommand.Check;
import google.registry.model.domain.DomainResource; 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.FeeCheckCommandExtensionItem;
import google.registry.model.domain.fee.FeeCheckResponseExtensionItem; import google.registry.model.domain.fee.FeeCheckResponseExtensionItem;
import google.registry.model.domain.launch.LaunchCheckExtension; import google.registry.model.domain.launch.LaunchCheckExtension;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CheckData.DomainCheck; import google.registry.model.eppoutput.CheckData.DomainCheck;
import google.registry.model.eppoutput.CheckData.DomainCheckData; 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.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservationType; import google.registry.model.registry.label.ReservationType;
import java.util.Collections; import google.registry.util.Clock;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that checks whether a domain can be provisioned. * 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 DomainFlowUtils.UnknownFeeCommandException}
* @error {@link OnlyCheckedNamesCanBeFeeCheckedException} * @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 * 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 ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @Config("maxChecks") int maxChecks; @Inject @Config("maxChecks") int maxChecks;
@Inject @Superuser boolean isSuperuser;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainCheckFlow() {} @Inject DomainCheckFlow() {}
@Override @Override
public EppOutput run() throws EppException { public EppResponse run() throws EppException {
extensionManager.register(LaunchCheckExtension.class); extensionManager.register(LaunchCheckExtension.class);
extensionManager.registerAsGroup(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.registerAsGroup(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); List<String> targetIds = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(targetIds, maxChecks);
DateTime now = clock.nowUtc();
ImmutableMap.Builder<String, InternetDomainName> domains = new ImmutableMap.Builder<>(); ImmutableMap.Builder<String, InternetDomainName> domains = new ImmutableMap.Builder<>();
// Only check that the registrar has access to a TLD the first time it is encountered // Only check that the registrar has access to a TLD the first time it is encountered
Set<String> seenTlds = new HashSet<>(); Set<String> seenTlds = new HashSet<>();
@ -133,17 +138,17 @@ public final class DomainCheckFlow extends Flow {
Set<String> existingIds = checkResourcesExist(DomainResource.class, targetIds, now); Set<String> existingIds = checkResourcesExist(DomainResource.class, targetIds, now);
ImmutableList.Builder<DomainCheck> checks = new ImmutableList.Builder<>(); ImmutableList.Builder<DomainCheck> checks = new ImmutableList.Builder<>();
for (String targetId : targetIds) { 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)); checks.add(DomainCheck.create(message == null, targetId, message));
} }
return createOutput( return responseBuilder
SUCCESS, .setResData(DomainCheckData.create(checks.build()))
DomainCheckData.create(checks.build()), .setExtensions(getResponseExtensions(domainNames, now))
getResponseExtensions(domainNames)); .build();
} }
private String getMessageForCheck( private String getMessageForCheck(
InternetDomainName domainName, Set<String> existingIds) { InternetDomainName domainName, Set<String> existingIds, DateTime now) {
if (existingIds.contains(domainName.toString())) { if (existingIds.contains(domainName.toString())) {
return "In use"; return "In use";
} }
@ -161,9 +166,7 @@ public final class DomainCheckFlow extends Flow {
if (reservationType == UNRESERVED if (reservationType == UNRESERVED
&& isDomainPremium(domainName.toString(), now) && isDomainPremium(domainName.toString(), now)
&& registry.getPremiumPriceAckRequired() && registry.getPremiumPriceAckRequired()
&& Collections.disjoint( && eppInput.getSingleExtension(FeeCheckCommandExtension.class) == null) {
nullToEmpty(sessionMetadata.getServiceExtensionUris()),
FEE_EXTENSION_URIS)) {
return "Premium names require EPP ext."; return "Premium names require EPP ext.";
} }
return reservationType.getMessageForCheck(); return reservationType.getMessageForCheck();
@ -172,7 +175,7 @@ public final class DomainCheckFlow extends Flow {
/** Handle the fee check extension. */ /** Handle the fee check extension. */
private ImmutableList<? extends ResponseExtension> getResponseExtensions( private ImmutableList<? extends ResponseExtension> getResponseExtensions(
ImmutableMap<String, InternetDomainName> domainNames) throws EppException { ImmutableMap<String, InternetDomainName> domainNames, DateTime now) throws EppException {
FeeCheckCommandExtension<?, ?> feeCheck = FeeCheckCommandExtension<?, ?> feeCheck =
eppInput.getFirstExtensionOfClasses(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); eppInput.getFirstExtensionOfClasses(FEE_CHECK_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
if (feeCheck == null) { if (feeCheck == null) {

View file

@ -54,8 +54,8 @@ import google.registry.flows.EppException;
import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; 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.rgp.GracePeriodStatus;
import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.domain.secdns.SecDnsCreateExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.DomainCreateData; import google.registry.model.eppoutput.CreateData.DomainCreateData;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.eppoutput.Result;
import google.registry.model.index.EppResourceIndex; import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.ofy.ObjectifyService; import google.registry.model.ofy.ObjectifyService;
@ -149,21 +149,24 @@ import org.joda.time.DateTime;
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException} * @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
*/ */
public class DomainCreateFlow extends Flow implements TransactionalFlow { public class DomainCreateFlow implements TransactionalFlow {
private static final Set<TldState> QLP_SMD_ALLOWED_STATES = private static final Set<TldState> QLP_SMD_ALLOWED_STATES =
Sets.immutableEnumSet(TldState.SUNRISE, TldState.SUNRUSH); Sets.immutableEnumSet(TldState.SUNRISE, TldState.SUNRUSH);
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject AuthInfo authInfo; @Inject AuthInfo authInfo;
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainCreateFlow() {} @Inject DomainCreateFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
SecDnsCreateExtension.class, SecDnsCreateExtension.class,
FlagsCreateCommandExtension.class, FlagsCreateCommandExtension.class,
@ -172,6 +175,7 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow {
extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.registerAsGroup(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
Create command = cloneAndLinkReferences((Create) resourceCommand, now); Create command = cloneAndLinkReferences((Create) resourceCommand, now);
Period period = command.getPeriod(); Period period = command.getPeriod();
verifyUnitIsYears(period); verifyUnitIsYears(period);
@ -219,17 +223,17 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow {
verifyClaimsNoticeIfAndOnlyIfNeeded(domainName, hasSignedMarks, hasClaimsNotice); verifyClaimsNoticeIfAndOnlyIfNeeded(domainName, hasSignedMarks, hasClaimsNotice);
} }
verifyPremiumNameIsNotBlocked(targetId, now, clientId); verifyPremiumNameIsNotBlocked(targetId, now, clientId);
verifyNoOpenApplications(); verifyNoOpenApplications(now);
verifyIsGaOrIsSpecialCase(tldState, isAnchorTenant); verifyIsGaOrIsSpecialCase(tldState, isAnchorTenant);
} }
SecDnsCreateExtension secDnsCreate = SecDnsCreateExtension secDnsCreate =
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr()); String repoId = createDomainRoid(ObjectifyService.allocateId(), registry.getTldStr());
DateTime registrationExpirationTime = leapSafeAddYears(now, years); DateTime registrationExpirationTime = leapSafeAddYears(now, years);
HistoryEntry historyEntry = buildHistory(repoId, period); HistoryEntry historyEntry = buildHistory(repoId, period, now);
// Bill for the create. // Bill for the create.
BillingEvent.OneTime createBillingEvent = BillingEvent.OneTime createBillingEvent = createOneTimeBillingEvent(
createOneTimeBillingEvent(registry, isAnchorTenant, years, commandOperations, historyEntry); registry, isAnchorTenant, years, commandOperations, historyEntry, now);
// Create a new autorenew billing event and poll message starting at the expiration time. // Create a new autorenew billing event and poll message starting at the expiration time.
BillingEvent.Recurring autorenewBillingEvent = BillingEvent.Recurring autorenewBillingEvent =
createAutorenewBillingEvent(historyEntry, registrationExpirationTime); createAutorenewBillingEvent(historyEntry, registrationExpirationTime);
@ -266,7 +270,7 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow {
.setContacts(command.getContacts()) .setContacts(command.getContacts())
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createBillingEvent)) .addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createBillingEvent))
.build(); .build();
handleExtraFlowLogic(registry.getTldStr(), years, historyEntry, newDomain); handleExtraFlowLogic(registry.getTldStr(), years, historyEntry, newDomain, now);
entitiesToSave.add( entitiesToSave.add(
newDomain, newDomain,
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()), ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
@ -274,16 +278,16 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow {
// Anchor tenant registrations override LRP, and landrush applications can skip it. // 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 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( entitiesToSave.add(
prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry)); prepareMarkedLrpTokenEntity(authInfo.getPw().getValue(), domainName, historyEntry));
} }
enqueueTasks(hasSignedMarks, hasClaimsNotice, newDomain); enqueueTasks(hasSignedMarks, hasClaimsNotice, newDomain);
ofy().save().entities(entitiesToSave.build()); ofy().save().entities(entitiesToSave.build());
return createOutput( return responseBuilder
Result.Code.SUCCESS, .setResData(DomainCreateData.create(targetId, now, registrationExpirationTime))
DomainCreateData.create(targetId, now, registrationExpirationTime), .setExtensions(createResponseExtensions(feeCreate, commandOperations))
createResponseExtensions(feeCreate, commandOperations)); .build();
} }
private boolean isAnchorTenant(InternetDomainName domainName) { 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. */ /** 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)) { for (DomainApplication application : loadActiveApplicationsByDomainName(targetId, now)) {
if (!application.getApplicationStatus().isFinalStatus()) { if (!application.getApplicationStatus().isFinalStatus()) {
throw new DomainHasOpenApplicationsException(); 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 return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(period) .setPeriod(period)
@ -331,7 +335,8 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow {
boolean isAnchorTenant, boolean isAnchorTenant,
int years, int years,
EppCommandOperations commandOperations, EppCommandOperations commandOperations,
HistoryEntry historyEntry) { HistoryEntry historyEntry,
DateTime now) {
return new BillingEvent.OneTime.Builder() return new BillingEvent.OneTime.Builder()
.setReason(Reason.CREATE) .setReason(Reason.CREATE)
.setTargetId(targetId) .setTargetId(targetId)
@ -387,12 +392,12 @@ public class DomainCreateFlow extends Flow implements TransactionalFlow {
.build(); .build();
} }
private boolean isLrpCreate(Registry registry, boolean isAnchorTenant) { private boolean isLrpCreate(Registry registry, boolean isAnchorTenant, DateTime now) {
return registry.getLrpPeriod().contains(now) && !isAnchorTenant; return registry.getLrpPeriod().contains(now) && !isAnchorTenant;
} }
private void handleExtraFlowLogic( private void handleExtraFlowLogic(
String tld, int years, HistoryEntry historyEntry, DomainResource newDomain) String tld, int years, HistoryEntry historyEntry, DomainResource newDomain, DateTime now)
throws EppException { throws EppException {
Optional<RegistryExtraFlowLogic> extraFlowLogic = Optional<RegistryExtraFlowLogic> extraFlowLogic =
RegistryExtraFlowLogicProxy.newInstanceForTld(tld); RegistryExtraFlowLogicProxy.newInstanceForTld(tld);

View file

@ -40,9 +40,10 @@ import google.registry.dns.DnsQueue;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.AssociationProhibitsOperationException; import google.registry.flows.EppException.AssociationProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.SessionMetadata;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent; 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.AuthInfo;
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
import google.registry.model.eppcommon.StatusValue; 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.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.OneTime; import google.registry.model.poll.PollMessage.OneTime;
@ -84,7 +87,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException} * @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
*/ */
public final class DomainDeleteFlow extends Flow implements TransactionalFlow { public final class DomainDeleteFlow implements TransactionalFlow {
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.LINKED, StatusValue.LINKED,
@ -93,23 +96,29 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow {
StatusValue.SERVER_DELETE_PROHIBITED); StatusValue.SERVER_DELETE_PROHIBITED);
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject SessionMetadata sessionMetadata;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject Trid trid;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainDeleteFlow() {} @Inject DomainDeleteFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class, SecDnsCreateExtension.class); extensionManager.register(MetadataExtension.class, SecDnsCreateExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
// Loads the target resource if it exists // Loads the target resource if it exists
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
Registry registry = Registry.get(existingDomain.getTld()); Registry registry = Registry.get(existingDomain.getTld());
verifyDeleteAllowed(existingDomain, registry); verifyDeleteAllowed(existingDomain, registry, now);
HistoryEntry historyEntry = buildHistoryEntry(existingDomain); HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now);
Builder builder = (Builder) prepareDeletedResourceAsBuilder(existingDomain, now); Builder builder = (Builder) prepareDeletedResourceAsBuilder(existingDomain, now);
// If the domain is in the Add Grace Period, we delete it immediately, which is already // 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. // 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))) clientId)))
.setDeletePollMessage(Key.create(deletePollMessage)); .setDeletePollMessage(Key.create(deletePollMessage));
} }
handleExtraFlowLogic(existingDomain, historyEntry); handleExtraFlowLogic(existingDomain, historyEntry, now);
DomainResource newDomain = builder.build(); DomainResource newDomain = builder.build();
updateForeignKeyIndexDeletionTime(newDomain); updateForeignKeyIndexDeletionTime(newDomain);
handlePendingTransferOnDelete(existingDomain, newDomain, now, historyEntry); handlePendingTransferOnDelete(existingDomain, newDomain, now, historyEntry);
@ -150,13 +159,14 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow {
} }
} }
ofy().save().<ImmutableObject>entities(newDomain, historyEntry); ofy().save().<ImmutableObject>entities(newDomain, historyEntry);
return createOutput( return responseBuilder
newDomain.getDeletionTime().isAfter(now) ? SUCCESS_WITH_ACTION_PENDING : SUCCESS, .setResultFromCode(
null, newDomain.getDeletionTime().isAfter(now) ? SUCCESS_WITH_ACTION_PENDING : SUCCESS)
getResponseExtensions(existingDomain)); .setExtensions(getResponseExtensions(existingDomain, now))
.build();
} }
private void verifyDeleteAllowed(DomainResource existingDomain, Registry registry) private void verifyDeleteAllowed(DomainResource existingDomain, Registry registry, DateTime now)
throws EppException { throws EppException {
verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES);
verifyOptionalAuthInfoForResource(authInfo, existingDomain); 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 return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_DELETE) .setType(HistoryEntry.Type.DOMAIN_DELETE)
.setModificationTime(now) .setModificationTime(now)
@ -191,7 +201,8 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow {
.build(); .build();
} }
private void handleExtraFlowLogic(DomainResource existingResource, HistoryEntry historyEntry) private void handleExtraFlowLogic(
DomainResource existingResource, HistoryEntry historyEntry, DateTime now)
throws EppException { throws EppException {
Optional<RegistryExtraFlowLogic> extraFlowLogic = Optional<RegistryExtraFlowLogic> extraFlowLogic =
RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource); RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource);
@ -203,7 +214,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow {
@Nullable @Nullable
private ImmutableList<FeeTransformResponseExtension> getResponseExtensions( private ImmutableList<FeeTransformResponseExtension> getResponseExtensions(
DomainResource existingDomain) { DomainResource existingDomain, DateTime now) {
FeeTransformResponseExtension.Builder feeResponseBuilder = getDeleteResponseBuilder(); FeeTransformResponseExtension.Builder feeResponseBuilder = getDeleteResponseBuilder();
if (feeResponseBuilder == null) { if (feeResponseBuilder == null) {
return null; return null;
@ -211,7 +222,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow {
ImmutableList.Builder<Credit> creditsBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<Credit> creditsBuilder = new ImmutableList.Builder<>();
for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) { for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) {
if (gracePeriod.hasBillingEvent()) { if (gracePeriod.hasBillingEvent()) {
Money cost = getGracePeriodCost(gracePeriod); Money cost = getGracePeriodCost(gracePeriod, now);
creditsBuilder.add(Credit.create( creditsBuilder.add(Credit.create(
cost.negated().getAmount(), FeeType.CREDIT, gracePeriod.getType().getXmlName())); cost.negated().getAmount(), FeeType.CREDIT, gracePeriod.getType().getXmlName()));
feeResponseBuilder.setCurrency(checkNotNull(cost.getCurrencyUnit())); feeResponseBuilder.setCurrency(checkNotNull(cost.getCurrencyUnit()));
@ -224,7 +235,7 @@ public final class DomainDeleteFlow extends Flow implements TransactionalFlow {
return ImmutableList.of(feeResponseBuilder.setCredits(credits).build()); 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) { if (gracePeriod.getType() == GracePeriodStatus.AUTO_RENEW) {
DateTime autoRenewTime = DateTime autoRenewTime =
ofy().load().key(checkNotNull(gracePeriod.getRecurringBillingEvent())).now() ofy().load().key(checkNotNull(gracePeriod.getRecurringBillingEvent())).now()

View file

@ -19,7 +19,6 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent;
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; 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 static google.registry.util.CollectionUtils.forceEmptyToNull;
import com.google.common.base.Optional; 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.GracePeriodStatus;
import google.registry.model.domain.rgp.RgpInfoExtension; import google.registry.model.domain.rgp.RgpInfoExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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.eppoutput.EppResponse.ResponseExtension;
import google.registry.util.Clock;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that returns information about a domain. * An EPP flow that returns information about a domain.
@ -61,26 +63,30 @@ import javax.inject.Inject;
* @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException} * @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException}
* @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException} * @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException}
*/ */
public final class DomainInfoFlow extends Flow { public final class DomainInfoFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject ResourceCommand resourceCommand;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject ResourceCommand resourceCommand; @Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainInfoFlow() {} @Inject DomainInfoFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(FeeInfoCommandExtensionV06.class); extensionManager.register(FeeInfoCommandExtensionV06.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = clock.nowUtc();
DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, domain); verifyOptionalAuthInfoForResource(authInfo, domain);
return createOutput( return responseBuilder
SUCCESS, .setResData(getResourceInfo(domain))
getResourceInfo(domain), .setExtensions(getDomainResponseExtensions(domain, now))
getDomainResponseExtensions(domain)); .build();
} }
private DomainResource getResourceInfo(DomainResource domain) { private DomainResource getResourceInfo(DomainResource domain) {
@ -111,8 +117,8 @@ public final class DomainInfoFlow extends Flow {
return info.build(); return info.build();
} }
private ImmutableList<ResponseExtension> getDomainResponseExtensions(DomainResource domain) private ImmutableList<ResponseExtension> getDomainResponseExtensions(
throws EppException { DomainResource domain, DateTime now) throws EppException {
ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>(); ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>();
addSecDnsExtensionIfPresent(extensions, domain.getDsData()); addSecDnsExtensionIfPresent(extensions, domain.getDsData());
ImmutableSet<GracePeriodStatus> gracePeriodStatuses = domain.getGracePeriodStatuses(); ImmutableSet<GracePeriodStatus> gracePeriodStatuses = domain.getGracePeriodStatuses();

View file

@ -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.MAX_REGISTRATION_YEARS;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; 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.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.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.leapSafeAddYears; 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.ObjectPendingTransferException;
import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.ParameterValueRangeErrorException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; 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.GracePeriodStatus;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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.poll.PollMessage;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
@ -97,7 +97,7 @@ import org.joda.time.DateTime;
* @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException} * @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException}
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException} * @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
*/ */
public final class DomainRenewFlow extends Flow implements TransactionalFlow { public final class DomainRenewFlow implements TransactionalFlow {
private static final ImmutableSet<StatusValue> RENEW_DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> RENEW_DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.CLIENT_RENEW_PROHIBITED, StatusValue.CLIENT_RENEW_PROHIBITED,
@ -106,18 +106,22 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainRenewFlow() {} @Inject DomainRenewFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.registerAsGroup(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.registerAsGroup(FEE_RENEW_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
Renew command = (Renew) resourceCommand; Renew command = (Renew) resourceCommand;
// Loads the target resource if it exists // Loads the target resource if it exists
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
@ -143,7 +147,7 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow {
String tld = existingDomain.getTld(); String tld = existingDomain.getTld();
// Bill for this explicit renew itself. // Bill for this explicit renew itself.
BillingEvent.OneTime explicitRenewEvent = 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. // Create a new autorenew billing event and poll message starting at the new expiration time.
BillingEvent.Recurring newAutorenewEvent = newAutorenewBillingEvent(existingDomain) BillingEvent.Recurring newAutorenewEvent = newAutorenewBillingEvent(existingDomain)
.setEventTime(newExpirationTime) .setEventTime(newExpirationTime)
@ -170,10 +174,10 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow {
.build(); .build();
ofy().save().<Object>entities( ofy().save().<Object>entities(
newDomain, historyEntry, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage); newDomain, historyEntry, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
return createOutput( return responseBuilder
SUCCESS, .setResData(DomainRenewData.create(targetId, newExpirationTime))
DomainRenewData.create(targetId, newExpirationTime), .setExtensions(createResponseExtensions(commandOperations.getTotalCost(), feeRenew))
createResponseExtensions(commandOperations.getTotalCost(), feeRenew)); .build();
} }
private void verifyRenewAllowed( private void verifyRenewAllowed(
@ -199,7 +203,7 @@ public final class DomainRenewFlow extends Flow implements TransactionalFlow {
} }
private OneTime createRenewBillingEvent( 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() return new BillingEvent.OneTime.Builder()
.setReason(Reason.RENEW) .setReason(Reason.RENEW)
.setTargetId(targetId) .setTargetId(targetId)

View file

@ -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.verifyNotReserved;
import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; 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.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.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.END_OF_TIME; 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.CommandUseErrorException;
import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; 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.GracePeriodStatus;
import google.registry.model.domain.rgp.RgpUpdateExtension; import google.registry.model.domain.rgp.RgpUpdateExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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.poll.PollMessage;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
@ -103,37 +103,40 @@ import org.joda.time.DateTime;
* @error {@link DomainRestoreRequestFlow.DomainNotEligibleForRestoreException} * @error {@link DomainRestoreRequestFlow.DomainNotEligibleForRestoreException}
* @error {@link DomainRestoreRequestFlow.RestoreCommandIncludesChangesException} * @error {@link DomainRestoreRequestFlow.RestoreCommandIncludesChangesException}
*/ */
public final class DomainRestoreRequestFlow extends Flow implements TransactionalFlow { public final class DomainRestoreRequestFlow implements TransactionalFlow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainRestoreRequestFlow() {} @Inject DomainRestoreRequestFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class, RgpUpdateExtension.class); extensionManager.register(MetadataExtension.class, RgpUpdateExtension.class);
extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
Update command = (Update) resourceCommand; Update command = (Update) resourceCommand;
DateTime now = ofy().getTransactionTime();
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
Money restoreCost = Registry.get(existingDomain.getTld()).getStandardRestoreCost(); Money restoreCost = Registry.get(existingDomain.getTld()).getStandardRestoreCost();
EppCommandOperations renewCommandOperations = TldSpecificLogicProxy.getRenewPrice( EppCommandOperations renewCommandOperations = TldSpecificLogicProxy.getRenewPrice(
Registry.get(existingDomain.getTld()), targetId, clientId, now, 1, eppInput); Registry.get(existingDomain.getTld()), targetId, clientId, now, 1, eppInput);
FeeTransformCommandExtension feeUpdate = eppInput.getFirstExtensionOfClasses( FeeTransformCommandExtension feeUpdate = eppInput.getFirstExtensionOfClasses(
FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
verifyRestoreAllowed( Money totalCost = renewCommandOperations.getTotalCost();
command, existingDomain, restoreCost, renewCommandOperations.getTotalCost(), feeUpdate); verifyRestoreAllowed(command, existingDomain, restoreCost, totalCost, feeUpdate, now);
HistoryEntry historyEntry = buildHistory(existingDomain); HistoryEntry historyEntry = buildHistory(existingDomain, now);
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>(); ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
entitiesToSave.addAll( entitiesToSave.addAll(
createRestoreAndRenewBillingEvents( createRestoreAndRenewBillingEvents(historyEntry, restoreCost, totalCost, now));
historyEntry, restoreCost, renewCommandOperations.getTotalCost()));
// We don't preserve the original expiration time of the domain when we restore, since doing so // 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, // 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 // 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().save().entities(entitiesToSave.build());
ofy().delete().key(existingDomain.getDeletePollMessage()); ofy().delete().key(existingDomain.getDeletePollMessage());
dnsQueue.addDomainRefreshTask(existingDomain.getFullyQualifiedDomainName()); dnsQueue.addDomainRefreshTask(existingDomain.getFullyQualifiedDomainName());
return createOutput( return responseBuilder
SUCCESS, .setExtensions(createResponseExtensions(restoreCost, totalCost, feeUpdate))
null, .build();
createResponseExtensions(restoreCost, renewCommandOperations.getTotalCost(), feeUpdate));
} }
private HistoryEntry buildHistory(DomainResource existingDomain) { private HistoryEntry buildHistory(DomainResource existingDomain, DateTime now) {
return historyBuilder return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_RESTORE) .setType(HistoryEntry.Type.DOMAIN_RESTORE)
.setModificationTime(now) .setModificationTime(now)
@ -182,7 +184,8 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona
DomainResource existingDomain, DomainResource existingDomain,
Money restoreCost, Money restoreCost,
Money renewCost, Money renewCost,
FeeTransformCommandExtension feeUpdate) throws EppException { FeeTransformCommandExtension feeUpdate,
DateTime now) throws EppException {
verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyOptionalAuthInfoForResource(authInfo, existingDomain);
if (!isSuperuser) { if (!isSuperuser) {
verifyResourceOwnership(clientId, existingDomain); verifyResourceOwnership(clientId, existingDomain);
@ -202,14 +205,14 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona
} }
private ImmutableSet<BillingEvent.OneTime> createRestoreAndRenewBillingEvents( private ImmutableSet<BillingEvent.OneTime> createRestoreAndRenewBillingEvents(
HistoryEntry historyEntry, Money restoreCost, Money renewCost) { HistoryEntry historyEntry, Money restoreCost, Money renewCost, DateTime now) {
// Bill for the restore. // 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. // 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 // 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 // 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. // 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); return ImmutableSet.of(restoreEvent, renewEvent);
} }
@ -229,21 +232,22 @@ public final class DomainRestoreRequestFlow extends Flow implements Transactiona
.build(); .build();
} }
private OneTime createRenewBillingEvent(HistoryEntry historyEntry, Money renewCost) { private OneTime createRenewBillingEvent(
return prepareBillingEvent(historyEntry, renewCost) HistoryEntry historyEntry, Money renewCost, DateTime now) {
return prepareBillingEvent(historyEntry, renewCost, now)
.setPeriodYears(1) .setPeriodYears(1)
.setReason(Reason.RENEW) .setReason(Reason.RENEW)
.build(); .build();
} }
private BillingEvent.OneTime createRestoreBillingEvent( private BillingEvent.OneTime createRestoreBillingEvent(
HistoryEntry historyEntry, Money restoreCost) { HistoryEntry historyEntry, Money restoreCost, DateTime now) {
return prepareBillingEvent(historyEntry, restoreCost) return prepareBillingEvent(historyEntry, restoreCost, now)
.setReason(Reason.RESTORE) .setReason(Reason.RESTORE)
.build(); .build();
} }
private Builder prepareBillingEvent(HistoryEntry historyEntry, Money cost) { private Builder prepareBillingEvent(HistoryEntry historyEntry, Money cost, DateTime now) {
return new BillingEvent.OneTime.Builder() return new BillingEvent.OneTime.Builder()
.setTargetId(targetId) .setTargetId(targetId)
.setClientId(clientId) .setClientId(clientId)

View file

@ -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.createTransferResponse;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; 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.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost; import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.DateTimeUtils.END_OF_TIME; 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 com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.metadata.MetadataExtension;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.AuthInfo; 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.poll.PollMessage;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
@ -78,13 +76,14 @@ import org.joda.time.DateTime;
* @error {@link google.registry.flows.exceptions.NotPendingTransferException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
*/ */
public final class DomainTransferApproveFlow extends Flow implements TransactionalFlow { public final class DomainTransferApproveFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainTransferApproveFlow() {} @Inject DomainTransferApproveFlow() {}
/** /**
@ -92,10 +91,11 @@ public final class DomainTransferApproveFlow extends Flow implements Transaction
* {@link DomainResource#cloneProjectedAtTime} which handles implicit server approvals. * {@link DomainResource#cloneProjectedAtTime} which handles implicit server approvals.
*/ */
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyOptionalAuthInfoForResource(authInfo, existingDomain);
verifyHasPendingTransfer(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 // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities()); ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities());
return createOutput( return responseBuilder
SUCCESS, .setResData(createTransferResponse(
createTransferResponse( targetId, newDomain.getTransferData(), newDomain.getRegistrationExpirationTime()))
targetId, newDomain.getTransferData(), newDomain.getRegistrationExpirationTime())); .build();
} }
} }

View file

@ -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.createLosingTransferPollMessage;
import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; 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.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.END_OF_TIME; 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 com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.DomainResource;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that cancels a pending transfer on a domain. * 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 google.registry.flows.exceptions.NotTransferInitiatorException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
*/ */
public final class DomainTransferCancelFlow extends Flow implements TransactionalFlow { public final class DomainTransferCancelFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainTransferCancelFlow() {} @Inject DomainTransferCancelFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyOptionalAuthInfoForResource(authInfo, existingDomain);
verifyHasPendingTransfer(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 // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities()); ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities());
return createOutput( return responseBuilder
SUCCESS, .setResData(createTransferResponse(targetId, newDomain.getTransferData(), null))
createTransferResponse(targetId, newDomain.getTransferData(), null)); .build();
} }
} }

View file

@ -19,7 +19,6 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -31,9 +30,10 @@ import google.registry.flows.exceptions.NoTransferHistoryToQueryException;
import google.registry.flows.exceptions.NotAuthorizedToViewTransferException; import google.registry.flows.exceptions.NotAuthorizedToViewTransferException;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.model.eppcommon.AuthInfo; 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.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; 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.NoTransferHistoryToQueryException}
* @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException} * @error {@link google.registry.flows.exceptions.NotAuthorizedToViewTransferException}
*/ */
public final class DomainTransferQueryFlow extends Flow { public final class DomainTransferQueryFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainTransferQueryFlow() {} @Inject DomainTransferQueryFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = clock.nowUtc();
DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource domain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, domain); verifyOptionalAuthInfoForResource(authInfo, domain);
// Most of the fields on the transfer response are required, so there's no way to return valid // 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(), domain.getRegistrationExpirationTime(),
transferData.getExtendedRegistrationYears()); transferData.getExtendedRegistrationYears());
} }
return createOutput(SUCCESS, createTransferResponse(targetId, transferData, newExpirationTime)); return responseBuilder
.setResData(createTransferResponse(targetId, transferData, newExpirationTime))
.build();
} }
} }

View file

@ -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.createGainingTransferPollMessage;
import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; 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.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.END_OF_TIME; 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 com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.DomainResource;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that rejects a pending transfer on a domain. * 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 google.registry.flows.exceptions.NotPendingTransferException}
* @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException}
*/ */
public final class DomainTransferRejectFlow extends Flow implements TransactionalFlow { public final class DomainTransferRejectFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainTransferRejectFlow() {} @Inject DomainTransferRejectFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
HistoryEntry historyEntry = historyBuilder HistoryEntry historyEntry = historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REJECT) .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 // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities()); ofy().delete().keys(existingDomain.getTransferData().getServerApproveEntities());
return createOutput( return responseBuilder
SUCCESS, .setResData(createTransferResponse(targetId, newDomain.getTransferData(), null))
createTransferResponse(targetId, newDomain.getTransferData(), null)); .build();
} }
} }

View file

@ -41,8 +41,8 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.exceptions.AlreadyPendingTransferException; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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.poll.PollMessage;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
@ -106,7 +108,7 @@ import org.joda.time.Duration;
* @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException} * @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
*/ */
public final class DomainTransferRequestFlow extends Flow implements TransactionalFlow { public final class DomainTransferRequestFlow implements TransactionalFlow {
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.CLIENT_TRANSFER_PROHIBITED, StatusValue.CLIENT_TRANSFER_PROHIBITED,
@ -115,22 +117,27 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String gainingClientId; @Inject @ClientId String gainingClientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject Trid trid;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainTransferRequestFlow() {} @Inject DomainTransferRequestFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(FlagsTransferCommandExtension.class, MetadataExtension.class); extensionManager.register(FlagsTransferCommandExtension.class, MetadataExtension.class);
extensionManager.registerAsGroup(FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); extensionManager.registerAsGroup(FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(gainingClientId); validateClientIsLoggedIn(gainingClientId);
Period period = ((Transfer) resourceCommand).getPeriod(); Period period = ((Transfer) resourceCommand).getPeriod();
int years = period.getValue(); int years = period.getValue();
DateTime now = ofy().getTransactionTime();
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyTransferAllowed(existingDomain, period); verifyTransferAllowed(existingDomain, period, now);
String tld = existingDomain.getTld(); String tld = existingDomain.getTld();
Registry registry = Registry.get(tld); Registry registry = Registry.get(tld);
// The cost of the renewal implied by a transfer. // 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( FeeTransformCommandExtension feeTransfer = eppInput.getFirstExtensionOfClasses(
FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
validateFeeChallenge(targetId, tld, now, feeTransfer, renewCost); validateFeeChallenge(targetId, tld, now, feeTransfer, renewCost);
HistoryEntry historyEntry = buildHistory(period, existingDomain); HistoryEntry historyEntry = buildHistory(period, existingDomain, now);
DateTime automaticTransferTime = now.plus(registry.getAutomaticTransferLength()); DateTime automaticTransferTime = now.plus(registry.getAutomaticTransferLength());
// The new expiration time if there is a server approval. // The new expiration time if there is a server approval.
DateTime serverApproveNewExpirationTime = extendRegistrationWithCap( DateTime serverApproveNewExpirationTime = extendRegistrationWithCap(
@ -152,10 +159,11 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction
historyEntry, historyEntry,
existingDomain, existingDomain,
renewCost, renewCost,
years); years,
now);
// Create the transfer data that represents the pending transfer. // Create the transfer data that represents the pending transfer.
TransferData pendingTransferData = createPendingTransferData( TransferData pendingTransferData = createPendingTransferData(
createTransferDataBuilder(existingDomain, automaticTransferTime, years), createTransferDataBuilder(existingDomain, automaticTransferTime, years, now),
serverApproveEntities); serverApproveEntities);
// Create a poll message to notify the losing registrar that a transfer was requested. // Create a poll message to notify the losing registrar that a transfer was requested.
PollMessage requestPollMessage = createLosingTransferPollMessage( 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 // cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones
// that we've created in this flow and stored in pendingTransferData. // that we've created in this flow and stored in pendingTransferData.
updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime); updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime);
handleExtraFlowLogic(years, existingDomain, historyEntry); handleExtraFlowLogic(years, existingDomain, historyEntry, now);
DomainResource newDomain = existingDomain.asBuilder() DomainResource newDomain = existingDomain.asBuilder()
.setTransferData(pendingTransferData) .setTransferData(pendingTransferData)
.addStatusValue(StatusValue.PENDING_TRANSFER) .addStatusValue(StatusValue.PENDING_TRANSFER)
@ -177,13 +185,14 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction
.addAll(serverApproveEntities) .addAll(serverApproveEntities)
.build()) .build())
.now(); .now();
return createOutput( return responseBuilder
SUCCESS_WITH_ACTION_PENDING, .setResultFromCode(SUCCESS_WITH_ACTION_PENDING)
createResponse(period, existingDomain, newDomain), .setResData(createResponse(period, existingDomain, newDomain, now))
createResponseExtensions(renewCost, feeTransfer)); .setExtensions(createResponseExtensions(renewCost, feeTransfer))
.build();
} }
private void verifyTransferAllowed(DomainResource existingDomain, Period period) private void verifyTransferAllowed(DomainResource existingDomain, Period period, DateTime now)
throws EppException { throws EppException {
verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES);
verifyRequiredAuthInfoForResourceTransfer(authInfo, existingDomain); 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 return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST) .setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST)
.setPeriod(period) .setPeriod(period)
@ -231,10 +240,11 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction
HistoryEntry historyEntry, HistoryEntry historyEntry,
DomainResource existingDomain, DomainResource existingDomain,
Money renewCost, Money renewCost,
int years) { int years,
DateTime now) {
// Create a TransferData for the server-approve case to use for the speculative poll messages. // Create a TransferData for the server-approve case to use for the speculative poll messages.
TransferData serverApproveTransferData = TransferData serverApproveTransferData =
createTransferDataBuilder(existingDomain, automaticTransferTime, years) createTransferDataBuilder(existingDomain, automaticTransferTime, years, now)
.setTransferStatus(TransferStatus.SERVER_APPROVED) .setTransferStatus(TransferStatus.SERVER_APPROVED)
.build(); .build();
Registry registry = Registry.get(existingDomain.getTld()); Registry registry = Registry.get(existingDomain.getTld());
@ -333,9 +343,7 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction
} }
private Builder createTransferDataBuilder( private Builder createTransferDataBuilder(
DomainResource existingDomain, DomainResource existingDomain, DateTime automaticTransferTime, int years, DateTime now) {
DateTime automaticTransferTime,
int years) {
return new TransferData.Builder() return new TransferData.Builder()
.setTransferRequestTrid(trid) .setTransferRequestTrid(trid)
.setTransferRequestTime(now) .setTransferRequestTime(now)
@ -366,17 +374,23 @@ public final class DomainTransferRequestFlow extends Flow implements Transaction
} }
private void handleExtraFlowLogic( private void handleExtraFlowLogic(
int years, DomainResource existingDomain, HistoryEntry historyEntry) throws EppException { int years, DomainResource existingDomain, HistoryEntry historyEntry, DateTime now)
throws EppException {
Optional<RegistryExtraFlowLogic> extraFlowLogic = Optional<RegistryExtraFlowLogic> extraFlowLogic =
RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain); RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain);
if (extraFlowLogic.isPresent()) { if (extraFlowLogic.isPresent()) {
extraFlowLogic.get().performAdditionalDomainTransferLogic( extraFlowLogic.get().performAdditionalDomainTransferLogic(
existingDomain, gainingClientId, now, years, eppInput, historyEntry); existingDomain,
gainingClientId,
now,
years,
eppInput,
historyEntry);
} }
} }
private DomainTransferResponse createResponse( 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, // 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 // 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. // approval new expiration time, which is capped at 10 years from the server approve time.

View file

@ -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.verifyClientUpdateNotProhibited;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; 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.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.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.earliestOf;
@ -49,8 +48,8 @@ import com.googlecode.objectify.Key;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeUpdateException; 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.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand; 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;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
@ -121,7 +121,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.TooManyNameserversException} * @error {@link DomainFlowUtils.TooManyNameserversException}
* @error {@link DomainFlowUtils.UrgentAttributeNotSupportedException} * @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 * 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 ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject EppResponse.Builder responseBuilder;
@Inject DomainUpdateFlow() {} @Inject DomainUpdateFlow() {}
@Override @Override
public EppOutput run() throws EppException { public EppResponse run() throws EppException {
extensionManager.register( extensionManager.register(
FlagsUpdateCommandExtension.class, FlagsUpdateCommandExtension.class,
MetadataExtension.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.registerAsGroup(FEE_UPDATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
Update command = cloneAndLinkReferences((Update) resourceCommand, now); Update command = cloneAndLinkReferences((Update) resourceCommand, now);
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now); DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
verifyUpdateAllowed(command, existingDomain); verifyUpdateAllowed(command, existingDomain, now);
HistoryEntry historyEntry = buildHistoryEntry(existingDomain); HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now);
DomainResource newDomain = performUpdate(command, existingDomain); 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 // 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. // have added nameserver or removed holds, we have to convert it to a standard add grace period.
if (newDomain.shouldPublishToDns()) { if (newDomain.shouldPublishToDns()) {
for (GracePeriod gracePeriod : newDomain.getGracePeriods()) { for (GracePeriod gracePeriod : newDomain.getGracePeriods()) {
if (gracePeriod.isSunrushAddGracePeriod()) { if (gracePeriod.isSunrushAddGracePeriod()) {
newDomain = convertSunrushAddToAdd(newDomain, gracePeriod, historyEntry); newDomain = convertSunrushAddToAdd(newDomain, gracePeriod, historyEntry, now);
break; // There can only be one sunrush add grace period. break; // There can only be one sunrush add grace period.
} }
} }
} }
validateNewState(newDomain); validateNewState(newDomain);
dnsQueue.addDomainRefreshTask(targetId); dnsQueue.addDomainRefreshTask(targetId);
handleExtraFlowLogic(existingDomain, historyEntry); handleExtraFlowLogic(existingDomain, historyEntry, now);
ImmutableList.Builder<ImmutableObject> entitiesToSave = new ImmutableList.Builder<>(); ImmutableList.Builder<ImmutableObject> entitiesToSave = new ImmutableList.Builder<>();
entitiesToSave.add(newDomain, historyEntry); entitiesToSave.add(newDomain, historyEntry);
Optional<BillingEvent.OneTime> statusUpdateBillingEvent = Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry); createBillingEventForStatusUpdates(existingDomain, newDomain, historyEntry, now);
if (statusUpdateBillingEvent.isPresent()) { if (statusUpdateBillingEvent.isPresent()) {
entitiesToSave.add(statusUpdateBillingEvent.get()); entitiesToSave.add(statusUpdateBillingEvent.get());
} }
ofy().save().entities(entitiesToSave.build()); ofy().save().entities(entitiesToSave.build());
return createOutput(SUCCESS); return responseBuilder.build();
} }
/** Fail if the object doesn't exist or was deleted. */ /** 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 { throws EppException {
verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES);
verifyOptionalAuthInfoForResource(authInfo, existingDomain); verifyOptionalAuthInfoForResource(authInfo, existingDomain);
@ -219,7 +223,7 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow {
tld, add.getNameserverFullyQualifiedHostNames()); tld, add.getNameserverFullyQualifiedHostNames());
} }
private HistoryEntry buildHistoryEntry(DomainResource existingDomain) { private HistoryEntry buildHistoryEntry(DomainResource existingDomain, DateTime now) {
return historyBuilder return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_UPDATE) .setType(HistoryEntry.Type.DOMAIN_UPDATE)
.setModificationTime(now) .setModificationTime(now)
@ -227,7 +231,7 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow {
.build(); .build();
} }
private DomainResource performUpdate(Update command, DomainResource domain) private DomainResource performUpdate(Update command, DomainResource domain, DateTime now)
throws EppException { throws EppException {
AddRemove add = command.getInnerAdd(); AddRemove add = command.getInnerAdd();
AddRemove remove = command.getInnerRemove(); AddRemove remove = command.getInnerRemove();
@ -255,12 +259,12 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow {
} }
private DomainResource convertSunrushAddToAdd( 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. // Cancel the billing event for the sunrush add and replace it with a new billing event.
BillingEvent.Cancellation billingEventCancellation = BillingEvent.Cancellation billingEventCancellation =
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId); BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId);
BillingEvent.OneTime billingEvent = BillingEvent.OneTime billingEvent =
createBillingEventForSunrushConversion(newDomain, historyEntry, gracePeriod); createBillingEventForSunrushConversion(newDomain, historyEntry, gracePeriod, now);
ofy().save().entities(billingEvent, billingEventCancellation); ofy().save().entities(billingEvent, billingEventCancellation);
// Modify the grace periods on the domain. // Modify the grace periods on the domain.
return newDomain.asBuilder() return newDomain.asBuilder()
@ -270,7 +274,10 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow {
} }
private BillingEvent.OneTime createBillingEventForSunrushConversion( 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 // 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 // sunrush add grace period expiration time (i.e. you can't get extra add grace period by
// setting a nameserver). // 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. */ /** Some status updates cost money. Bill only once no matter how many of them are changed. */
private Optional<BillingEvent.OneTime> createBillingEventForStatusUpdates( private Optional<BillingEvent.OneTime> createBillingEventForStatusUpdates(
DomainResource existingDomain, DomainResource newDomain, HistoryEntry historyEntry) { DomainResource existingDomain,
DomainResource newDomain,
HistoryEntry historyEntry,
DateTime now) {
MetadataExtension metadataExtension = eppInput.getSingleExtension(MetadataExtension.class); MetadataExtension metadataExtension = eppInput.getSingleExtension(MetadataExtension.class);
if (metadataExtension != null && metadataExtension.getRequestedByRegistrar()) { if (metadataExtension != null && metadataExtension.getRequestedByRegistrar()) {
for (StatusValue statusValue for (StatusValue statusValue
@ -326,8 +336,8 @@ public final class DomainUpdateFlow extends Flow implements TransactionalFlow {
return Optional.absent(); return Optional.absent();
} }
private void handleExtraFlowLogic(DomainResource existingDomain, HistoryEntry historyEntry) private void handleExtraFlowLogic(
throws EppException { DomainResource existingDomain, HistoryEntry historyEntry, DateTime now) throws EppException {
Optional<RegistryExtraFlowLogic> extraFlowLogic = Optional<RegistryExtraFlowLogic> extraFlowLogic =
RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain); RegistryExtraFlowLogicProxy.newInstanceForDomain(existingDomain);
if (extraFlowLogic.isPresent()) { if (extraFlowLogic.isPresent()) {

View file

@ -17,7 +17,6 @@ package google.registry.flows.host;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount; import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.checkResourcesExist;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import google.registry.config.ConfigModule.Config; 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.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CheckData.HostCheck; import google.registry.model.eppoutput.CheckData.HostCheck;
import google.registry.model.eppoutput.CheckData.HostCheckData; 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.HostCommand.Check;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.util.Clock;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
@ -42,26 +42,28 @@ import javax.inject.Inject;
* *
* @error {@link google.registry.flows.exceptions.TooManyResourceChecksException} * @error {@link google.registry.flows.exceptions.TooManyResourceChecksException}
*/ */
public final class HostCheckFlow extends Flow { public final class HostCheckFlow implements Flow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @Config("maxChecks") int maxChecks; @Inject @Config("maxChecks") int maxChecks;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject HostCheckFlow() {} @Inject HostCheckFlow() {}
@Override @Override
protected final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
List<String> targetIds = ((Check) resourceCommand).getTargetIds(); List<String> targetIds = ((Check) resourceCommand).getTargetIds();
verifyTargetIdCount(targetIds, maxChecks); verifyTargetIdCount(targetIds, maxChecks);
Set<String> existingIds = checkResourcesExist(HostResource.class, targetIds, now); Set<String> existingIds = checkResourcesExist(HostResource.class, targetIds, clock.nowUtc());
ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>(); ImmutableList.Builder<HostCheck> checks = new ImmutableList.Builder<>();
for (String id : targetIds) { for (String id : targetIds) {
boolean unused = !existingIds.contains(id); boolean unused = !existingIds.contains(id);
checks.add(HostCheck.create(unused, id, unused ? null : "In use")); 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();
} }
} }

View file

@ -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.validateHostName;
import static google.registry.flows.host.HostFlowUtils.verifyDomainIsSameRegistrar; import static google.registry.flows.host.HostFlowUtils.verifyDomainIsSameRegistrar;
import static google.registry.model.EppResourceUtils.createContactHostRoid; 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.model.ofy.ObjectifyService.ofy;
import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.isNullOrEmpty;
import static google.registry.util.CollectionUtils.union; 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.ParameterValueRangeErrorException;
import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.RequiredParameterMissingException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppinput.ResourceCommand; import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.HostCreateData; 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.HostCommand.Create;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.host.HostResource.Builder; 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.ofy.ObjectifyService;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that creates a new host. * An EPP flow that creates a new host.
@ -70,7 +69,7 @@ import javax.inject.Inject;
* @error {@link SubordinateHostMustHaveIpException} * @error {@link SubordinateHostMustHaveIpException}
* @error {@link UnexpectedExternalHostIpException} * @error {@link UnexpectedExternalHostIpException}
*/ */
public final class HostCreateFlow extends Flow implements TransactionalFlow { public final class HostCreateFlow implements TransactionalFlow {
@Inject ResourceCommand resourceCommand; @Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@ -78,14 +77,16 @@ public final class HostCreateFlow extends Flow implements TransactionalFlow {
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject EppResponse.Builder responseBuilder;
@Inject HostCreateFlow() {} @Inject HostCreateFlow() {}
@Override @Override
protected final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
Create command = (Create) resourceCommand; Create command = (Create) resourceCommand;
DateTime now = ofy().getTransactionTime();
verifyResourceDoesNotExist(HostResource.class, targetId, now); verifyResourceDoesNotExist(HostResource.class, targetId, now);
// The superordinate domain of the host object if creating an in-bailiwick host, or null if // 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 // 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); dnsQueue.addHostRefreshTask(targetId);
} }
ofy().save().entities(entitiesToSave); 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. */ /** Subordinate hosts must have an ip address. */

View file

@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.async.AsyncFlowEnqueuer; 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.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that deletes a host. * 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.ResourceStatusProhibitsOperationException}
* @error {@link google.registry.flows.exceptions.ResourceToDeleteIsReferencedException} * @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<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.LINKED, StatusValue.LINKED,
@ -76,15 +77,18 @@ public final class HostDeleteFlow extends Flow implements TransactionalFlow {
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject AsyncFlowEnqueuer asyncFlowEnqueuer; @Inject AsyncFlowEnqueuer asyncFlowEnqueuer;
@Inject EppResponse.Builder responseBuilder;
@Inject HostDeleteFlow() {} @Inject HostDeleteFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
failfastForAsyncDelete(targetId, now, HostResource.class, GET_NAMESERVERS); failfastForAsyncDelete(targetId, now, HostResource.class, GET_NAMESERVERS);
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
@ -100,6 +104,6 @@ public final class HostDeleteFlow extends Flow implements TransactionalFlow {
.setModificationTime(now) .setModificationTime(now)
.setParent(Key.create(existingHost)); .setParent(Key.create(existingHost));
ofy().save().<Object>entities(newHost, historyBuilder.build()); ofy().save().<Object>entities(newHost, historyBuilder.build());
return createOutput(SUCCESS_WITH_ACTION_PENDING); return responseBuilder.setResultFromCode(SUCCESS_WITH_ACTION_PENDING).build();
} }
} }

View file

@ -18,7 +18,6 @@ import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus; import static google.registry.model.EppResourceUtils.cloneResourceWithLinkedStatus;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import google.registry.flows.EppException; 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.ClientId;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.model.eppcommon.AuthInfo; 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.model.host.HostResource;
import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that returns information about a host. * An EPP flow that returns information about a host.
@ -39,20 +40,23 @@ import javax.inject.Inject;
* *
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
*/ */
public final class HostInfoFlow extends Flow { public final class HostInfoFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject HostInfoFlow() {} @Inject HostInfoFlow() {}
@Override @Override
public EppOutput run() throws EppException { public EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = clock.nowUtc();
HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now); HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, host); verifyOptionalAuthInfoForResource(authInfo, host);
return createOutput(SUCCESS, cloneResourceWithLinkedStatus(host, now)); return responseBuilder.setResData(cloneResourceWithLinkedStatus(host, now)).build();
} }
} }

View file

@ -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.lookupSuperordinateDomain;
import static google.registry.flows.host.HostFlowUtils.validateHostName; import static google.registry.flows.host.HostFlowUtils.validateHostName;
import static google.registry.flows.host.HostFlowUtils.verifyDomainIsSameRegistrar; 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.index.ForeignKeyIndex.loadAndGetKey;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.CollectionUtils.isNullOrEmpty; 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.RequiredParameterMissingException;
import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId; import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.TransactionalFlow; import google.registry.flows.TransactionalFlow;
import google.registry.flows.async.AsyncFlowEnqueuer; 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.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.ResourceCommand; 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;
import google.registry.model.host.HostCommand.Update.AddRemove; import google.registry.model.host.HostCommand.Update.AddRemove;
import google.registry.model.host.HostCommand.Update.Change; 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 google.registry.model.reporting.HistoryEntry;
import java.util.Objects; import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow that updates a host. * An EPP flow that updates a host.
@ -91,7 +91,7 @@ import javax.inject.Inject;
* @error {@link RenameHostToExternalRemoveIpException} * @error {@link RenameHostToExternalRemoveIpException}
* @error {@link RenameHostToSubordinateRequiresIpException} * @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 * 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> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject HistoryEntry.Builder historyBuilder;
@Inject AsyncFlowEnqueuer asyncFlowEnqueuer; @Inject AsyncFlowEnqueuer asyncFlowEnqueuer;
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject EppResponse.Builder responseBuilder;
@Inject HostUpdateFlow() {} @Inject HostUpdateFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
extensionManager.validate(); extensionManager.validate();
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
Update command = (Update) resourceCommand; Update command = (Update) resourceCommand;
Change change = command.getInnerChange(); Change change = command.getInnerChange();
String suppliedNewHostName = change.getFullyQualifiedHostName(); String suppliedNewHostName = change.getFullyQualifiedHostName();
DateTime now = ofy().getTransactionTime();
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
boolean isHostRename = suppliedNewHostName != null; boolean isHostRename = suppliedNewHostName != null;
String oldHostName = targetId; String oldHostName = targetId;
@ -169,7 +172,7 @@ public final class HostUpdateFlow extends Flow implements TransactionalFlow {
.setParent(Key.create(existingHost)) .setParent(Key.create(existingHost))
.build()); .build());
ofy().save().entities(entitiesToSave.build()); ofy().save().entities(entitiesToSave.build());
return createOutput(SUCCESS); return responseBuilder.build();
} }
private void verifyUpdateAllowed( private void verifyUpdateAllowed(

View file

@ -91,7 +91,7 @@ import java.util.Set;
public class FlowPicker { public class FlowPicker {
/** Marker class for unimplemented flows. */ /** 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. */ /** A function type that takes an {@link EppInput} and returns a {@link Flow} class. */
private abstract static class FlowProvider { private abstract static class FlowProvider {

View file

@ -17,7 +17,6 @@ package google.registry.flows.poll;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.poll.PollFlowUtils.getPollMessagesQuery; import static google.registry.flows.poll.PollFlowUtils.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.eppoutput.Result.Code.SUCCESS_WITH_NO_MESSAGES;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.isBeforeOrAt; 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.ParameterValueSyntaxErrorException;
import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.RequiredParameterMissingException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.PollMessageId; import google.registry.flows.FlowModule.PollMessageId;
import google.registry.flows.TransactionalFlow; 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.MessageQueueInfo;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessageExternalKeyConverter; import google.registry.model.poll.PollMessageExternalKeyConverter;
@ -55,15 +53,16 @@ import org.joda.time.DateTime;
* @error {@link PollAckFlow.MissingMessageIdException} * @error {@link PollAckFlow.MissingMessageIdException}
* @error {@link PollAckFlow.NotAuthorizedToAckMessageException} * @error {@link PollAckFlow.NotAuthorizedToAckMessageException}
*/ */
public class PollAckFlow extends Flow implements TransactionalFlow { public class PollAckFlow implements TransactionalFlow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @PollMessageId String messageId; @Inject @PollMessageId String messageId;
@Inject EppResponse.Builder responseBuilder;
@Inject PollAckFlow() {} @Inject PollAckFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
if (messageId.isEmpty()) { if (messageId.isEmpty()) {
@ -78,6 +77,8 @@ public class PollAckFlow extends Flow implements TransactionalFlow {
throw new InvalidMessageIdException(messageId); 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 // 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. // it as if it doesn't exist yet.
PollMessage pollMessage = ofy().load().key(pollMessageKey).now(); PollMessage pollMessage = ofy().load().key(pollMessageKey).now();
@ -128,17 +129,14 @@ public class PollAckFlow extends Flow implements TransactionalFlow {
messageCount--; messageCount--;
} }
if (messageCount <= 0) { if (messageCount <= 0) {
return createOutput(SUCCESS_WITH_NO_MESSAGES); return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build();
} }
return createOutput( return responseBuilder
SUCCESS, .setMessageQueueInfo(new MessageQueueInfo.Builder()
null, // responseData .setQueueLength(messageCount)
null, // responseExtensions .setMessageId(messageId)
MessageQueueInfo.create( .build())
null, // eventTime .build();
null, // msg
messageCount,
messageId));
} }
/** Registrar is not authorized to ack this message. */ /** Registrar is not authorized to ack this message. */

View file

@ -27,11 +27,13 @@ import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow; import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.PollMessageId; 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.MessageQueueInfo;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessageExternalKeyConverter; import google.registry.model.poll.PollMessageExternalKeyConverter;
import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime;
/** /**
* An EPP flow for requesting {@link PollMessage}s. * An EPP flow for requesting {@link PollMessage}s.
@ -44,34 +46,39 @@ import javax.inject.Inject;
* *
* @error {@link PollRequestFlow.UnexpectedMessageIdException} * @error {@link PollRequestFlow.UnexpectedMessageIdException}
*/ */
public class PollRequestFlow extends Flow { public class PollRequestFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @PollMessageId String messageId; @Inject @PollMessageId String messageId;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@Inject PollRequestFlow() {} @Inject PollRequestFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
if (!messageId.isEmpty()) { if (!messageId.isEmpty()) {
throw new UnexpectedMessageIdException(); throw new UnexpectedMessageIdException();
} }
// Return the oldest message from the queue. // Return the oldest message from the queue.
DateTime now = clock.nowUtc();
PollMessage pollMessage = getPollMessagesQuery(clientId, now).first().now(); PollMessage pollMessage = getPollMessagesQuery(clientId, now).first().now();
if (pollMessage == null) { if (pollMessage == null) {
return createOutput(SUCCESS_WITH_NO_MESSAGES); return responseBuilder.setResultFromCode(SUCCESS_WITH_NO_MESSAGES).build();
} }
return createOutput( return responseBuilder
SUCCESS_WITH_ACK_MESSAGE, .setResultFromCode(SUCCESS_WITH_ACK_MESSAGE)
forceEmptyToNull(pollMessage.getResponseData()), .setMessageQueueInfo(new MessageQueueInfo.Builder()
forceEmptyToNull(pollMessage.getResponseExtensions()), .setQueueDate(pollMessage.getEventTime())
MessageQueueInfo.create( .setMsg(pollMessage.getMsg())
pollMessage.getEventTime(), .setQueueLength(getPollMessagesQuery(clientId, now).count())
pollMessage.getMsg(), .setMessageId(PollMessage.EXTERNAL_KEY_CONVERTER.convert(Key.create(pollMessage)))
getPollMessagesQuery(clientId, now).count(), .build())
PollMessage.EXTERNAL_KEY_CONVERTER.convert(Key.create(pollMessage)))); .setMultipleResData(forceEmptyToNull(pollMessage.getResponseData()))
.setExtensions(forceEmptyToNull(pollMessage.getResponseExtensions()))
.build();
} }
/** Unexpected message id. */ /** Unexpected message id. */

View file

@ -17,19 +17,20 @@ package google.registry.flows.session;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow; import google.registry.flows.Flow;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.eppoutput.Greeting; import google.registry.model.eppoutput.Greeting;
import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
/** A flow for an Epp "hello". */ /** A flow for an Epp "hello". */
public class HelloFlow extends Flow { public class HelloFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Clock clock;
@Inject HelloFlow() {} @Inject HelloFlow() {}
@Override @Override
public EppOutput run() throws EppException { public Greeting run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
return EppOutput.create(Greeting.create(now)); return Greeting.create(clock.nowUtc());
} }
} }

View file

@ -30,13 +30,15 @@ import google.registry.flows.EppException.UnimplementedOptionException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow; import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; 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;
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; 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.Login;
import google.registry.model.eppinput.EppInput.Options; import google.registry.model.eppinput.EppInput.Options;
import google.registry.model.eppinput.EppInput.Services; import google.registry.model.eppinput.EppInput.Services;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.eppoutput.Result.Code;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import java.util.Set; import java.util.Set;
@ -62,7 +64,7 @@ import javax.inject.Inject;
* @error {@link LoginFlow.RegistrarAccountNotActiveException} * @error {@link LoginFlow.RegistrarAccountNotActiveException}
* @error {@link LoginFlow.UnsupportedLanguageException} * @error {@link LoginFlow.UnsupportedLanguageException}
*/ */
public class LoginFlow extends Flow { public class LoginFlow implements Flow {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); 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; private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3;
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject SessionMetadata sessionMetadata;
@Inject TransportCredentials credentials;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject EppResponse.Builder responseBuilder;
@Inject LoginFlow() {} @Inject LoginFlow() {}
/** Run the flow and log errors. */ /** Run the flow and log errors. */
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
try { try {
return runWithoutLogging(); return runWithoutLogging();
} catch (EppException e) { } 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. */ /** 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. extensionManager.validate(); // There are no legal extensions for this flow.
Login login = (Login) eppInput.getCommandWrapper().getCommand(); Login login = (Login) eppInput.getCommandWrapper().getCommand();
if (!clientId.isEmpty()) { if (!clientId.isEmpty()) {
@ -137,7 +143,7 @@ public class LoginFlow extends Flow {
sessionMetadata.resetFailedLoginAttempts(); sessionMetadata.resetFailedLoginAttempts();
sessionMetadata.setClientId(login.getClientId()); sessionMetadata.setClientId(login.getClientId());
sessionMetadata.setServiceExtensionUris(serviceExtensionUrisBuilder.build()); sessionMetadata.setServiceExtensionUris(serviceExtensionUrisBuilder.build());
return createOutput(Code.SUCCESS); return responseBuilder.build();
} }
/** Registrar with this client ID could not be found. */ /** Registrar with this client ID could not be found. */

View file

@ -21,7 +21,8 @@ import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.Flow; import google.registry.flows.Flow;
import google.registry.flows.FlowModule.ClientId; 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; import javax.inject.Inject;
/** /**
@ -29,17 +30,19 @@ import javax.inject.Inject;
* *
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException} * @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
*/ */
public class LogoutFlow extends Flow { public class LogoutFlow implements Flow {
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject SessionMetadata sessionMetadata;
@Inject EppResponse.Builder responseBuilder;
@Inject LogoutFlow() {} @Inject LogoutFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.validate(); // There are no legal extensions for this flow. extensionManager.validate(); // There are no legal extensions for this flow.
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
sessionMetadata.invalidate(); sessionMetadata.invalidate();
return createOutput(SUCCESS_AND_CLOSE); return responseBuilder.setResultFromCode(SUCCESS_AND_CLOSE).build();
} }
} }

View file

@ -197,6 +197,10 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting {
return this; return this;
} }
public Builder setResultFromCode(Result.Code resultCode) {
return setResult(Result.create(resultCode));
}
public Builder setResult(Result result) { public Builder setResult(Result result) {
getInstance().result = result; getInstance().result = result;
return this; return this;
@ -207,11 +211,19 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting {
return this; return this;
} }
public Builder setResData(@Nullable ImmutableList<? extends ResponseData> resData) { public Builder setResData(ResponseData onlyResData) {
return setMultipleResData(ImmutableList.of(onlyResData));
}
public Builder setMultipleResData(@Nullable ImmutableList<? extends ResponseData> resData) {
getInstance().resData = resData; getInstance().resData = resData;
return this; return this;
} }
public Builder setOnlyExtension(ResponseExtension onlyExtension) {
return setExtensions(ImmutableList.of(onlyExtension));
}
public Builder setExtensions(@Nullable ImmutableList<? extends ResponseExtension> extensions) { public Builder setExtensions(@Nullable ImmutableList<? extends ResponseExtension> extensions) {
getInstance().extensions = extensions; getInstance().extensions = extensions;
return this; return this;

View file

@ -14,10 +14,11 @@
package google.registry.model.poll; package google.registry.model.poll;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import javax.annotation.Nullable;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -40,32 +41,34 @@ public class MessageQueueInfo extends ImmutableObject {
@XmlAttribute(name = "id") @XmlAttribute(name = "id")
String messageId; String messageId;
public DateTime getQueueDate() { /** A builder for constructing a {@link MessageQueueInfo}, since it's immutable. */
return queueDate; public static class Builder extends Buildable.Builder<MessageQueueInfo> {
public Builder setQueueDate(DateTime queueDate) {
getInstance().queueDate = queueDate;
return this;
} }
public String getMsg() { public Builder setMsg(String msg) {
return msg; getInstance().msg = msg;
return this;
} }
public Integer getQueueLength() { public Builder setQueueLength(int queueLength) {
return queueLength; checkArgument(queueLength >= 0);
getInstance().queueLength = queueLength;
return this;
} }
public String getMessageId() { public Builder setMessageId(String messageId) {
return messageId; getInstance().messageId = messageId;
return this;
} }
public static MessageQueueInfo create( @Override
@Nullable DateTime queueDate, public MessageQueueInfo build() {
@Nullable String msg, checkNotNull(getInstance().messageId);
Integer queueLength, checkNotNull(getInstance().queueLength);
String messageId) { return super.build();
MessageQueueInfo instance = new MessageQueueInfo(); }
instance.queueDate = queueDate;
instance.msg = msg;
instance.queueLength = checkNotNull(queueLength);
instance.messageId = checkNotNull(messageId);
return instance;
} }
} }

View file

@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions; import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.appengine.api.users.User; import com.google.appengine.api.users.User;
import com.google.common.base.Joiner; 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.collect.Iterables;
import com.google.common.testing.TestLogHandler; import com.google.common.testing.TestLogHandler;
import google.registry.model.eppcommon.Trid; 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;
import google.registry.monitoring.whitebox.EppMetric; import google.registry.monitoring.whitebox.EppMetric;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
@ -66,27 +63,23 @@ public class FlowRunnerTest extends ShardableTestCase {
public void before() { public void before() {
Logger.getLogger(FlowRunner.class.getCanonicalName()).addHandler(handler); Logger.getLogger(FlowRunner.class.getCanonicalName()).addHandler(handler);
final EppOutput eppOutput = mock(EppOutput.class); final EppResponse eppResponse = mock(EppResponse.class);
EppResponse eppResponse = mock(EppResponse.class);
when(eppOutput.getResponse()).thenReturn(eppResponse);
flowRunner.clientId = "TheRegistrar"; flowRunner.clientId = "TheRegistrar";
flowRunner.clock = new FakeClock();
flowRunner.credentials = new PasswordOnlyTransportCredentials(); flowRunner.credentials = new PasswordOnlyTransportCredentials();
flowRunner.eppInput = new EppInput();
flowRunner.eppRequestSource = EppRequestSource.UNIT_TEST; flowRunner.eppRequestSource = EppRequestSource.UNIT_TEST;
flowRunner.flowProvider = flowRunner.flowProvider =
Providers.<Flow>of( Providers.<Flow>of(
new Flow() { new Flow() {
@Override @Override
protected EppOutput run() { public EppResponse run() {
return eppOutput; return eppResponse;
}}); }});
flowRunner.inputXmlBytes = "<xml/>".getBytes(UTF_8); flowRunner.inputXmlBytes = "<xml/>".getBytes(UTF_8);
flowRunner.isDryRun = false; flowRunner.isDryRun = false;
flowRunner.isSuperuser = false; flowRunner.isSuperuser = false;
flowRunner.isTransactional = false; flowRunner.isTransactional = false;
flowRunner.metric = EppMetric.builderForRequest("request-id-1", flowRunner.clock); flowRunner.metric = EppMetric.builderForRequest("request-id-1", new FakeClock());
flowRunner.sessionMetadata = flowRunner.sessionMetadata =
new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.<String>of()); new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.<String>of());
flowRunner.trid = Trid.create("client-123", "server-456"); flowRunner.trid = Trid.create("client-123", "server-456");

View file

@ -52,7 +52,6 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.eppoutput.Result;
import google.registry.model.eppoutput.Result.Code; import google.registry.model.eppoutput.Result.Code;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.ofy.RequestCapturingAsyncDatastoreService; import google.registry.model.ofy.RequestCapturingAsyncDatastoreService;
@ -443,8 +442,8 @@ public class DomainResourceTest extends EntityTestCase {
int numPreviousReads = RequestCapturingAsyncDatastoreService.getReads().size(); int numPreviousReads = RequestCapturingAsyncDatastoreService.getReads().size();
EppXmlTransformer.marshal( EppXmlTransformer.marshal(
EppOutput.create(new EppResponse.Builder() EppOutput.create(new EppResponse.Builder()
.setResult(Result.create(Code.SUCCESS)) .setResultFromCode(Code.SUCCESS)
.setResData(ImmutableList.of(domain)) .setResData(domain)
.setTrid(Trid.create(null, "abc")) .setTrid(Trid.create(null, "abc"))
.build()), .build()),
ValidationMode.STRICT); ValidationMode.STRICT);

View file

@ -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);
}
}

View file

@ -17,7 +17,6 @@ package google.registry.model.translators;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import google.registry.flows.EppXmlTransformer; import google.registry.flows.EppXmlTransformer;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
@ -51,9 +50,9 @@ public class StatusValueAdapterTest {
String marshalled = new String( String marshalled = new String(
EppXmlTransformer.marshal( EppXmlTransformer.marshal(
EppOutput.create(new EppResponse.Builder() EppOutput.create(new EppResponse.Builder()
.setResData(ImmutableList.of(new HostResource.Builder() .setResData(new HostResource.Builder()
.addStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED) .addStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED)
.build())) .build())
.build()), .build()),
ValidationMode.LENIENT), ValidationMode.LENIENT),
UTF_8); UTF_8);