Dagger, meet Flows. Flows, meet Dagger.

Daggerizes all of the EPP flows. This does not change anything yet
about the flows themselves, just how they are invoked, but after
this CL it's safe to @Inject things into flow classes.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=125382478
This commit is contained in:
cgoldfeder 2016-06-20 14:51:42 -07:00 committed by Ben McIlwain
parent 116bf1f4d6
commit c9a16f7f11
69 changed files with 973 additions and 292 deletions

View file

@ -15,12 +15,11 @@
package google.registry.flows; package google.registry.flows;
import static google.registry.flows.EppXmlTransformer.unmarshal; import static google.registry.flows.EppXmlTransformer.unmarshal;
import static google.registry.flows.picker.FlowPicker.getFlowClass;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import google.registry.flows.FlowModule.EppExceptionInProviderException;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
@ -43,6 +42,7 @@ public final class EppController {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
@Inject Clock clock; @Inject Clock clock;
@Inject FlowComponent.Builder flowComponentBuilder;
@Inject EppMetrics metrics; @Inject EppMetrics metrics;
@Inject EppController() {} @Inject EppController() {}
@ -54,57 +54,62 @@ public final class EppController {
boolean isDryRun, boolean isDryRun,
boolean isSuperuser, boolean isSuperuser,
byte[] inputXmlBytes) { byte[] inputXmlBytes) {
Trid trid = null;
try {
EppInput eppInput = unmarshal(EppInput.class, inputXmlBytes);
trid = Trid.create(eppInput.getCommandWrapper().getClTrid());
ImmutableList<String> targetIds = eppInput.getTargetIds();
metrics.setCommandName(eppInput.getCommandName());
metrics.setClientId(sessionMetadata.getClientId()); metrics.setClientId(sessionMetadata.getClientId());
metrics.setPrivilegeLevel(isSuperuser ? "SUPERUSER" : "NORMAL"); metrics.setPrivilegeLevel(isSuperuser ? "SUPERUSER" : "NORMAL");
if (!targetIds.isEmpty()) { try {
metrics.setEppTarget(Joiner.on(",").join(targetIds)); EppInput eppInput;
} try {
FlowRunner flowRunner = new FlowRunner( eppInput = unmarshal(EppInput.class, inputXmlBytes);
getFlowClass(eppInput),
eppInput,
trid,
sessionMetadata,
credentials,
eppRequestSource,
isDryRun,
isSuperuser,
inputXmlBytes,
metrics,
clock);
EppOutput eppOutput = flowRunner.run();
if (eppOutput.isResponse()) {
metrics.setEppStatus(eppOutput.getResponse().getResult().getCode());
}
return eppOutput;
} catch (EppException e) { } catch (EppException e) {
// The command failed. Send the client an error message. // Send the client an error message, with no clTRID since we couldn't unmarshal it.
metrics.setEppStatus(e.getResult().getCode()); metrics.setEppStatus(e.getResult().getCode());
return getErrorResponse(clock, e.getResult(), trid); return getErrorResponse(clock, e.getResult(), Trid.create(null));
} catch (Throwable e) { }
// Something bad and unexpected happened. Send the client a generic error, and log it. metrics.setCommandName(eppInput.getCommandName());
logger.severe(e, "Unexpected failure"); if (!eppInput.getTargetIds().isEmpty()) {
metrics.setEppStatus(Code.CommandFailed); metrics.setEppTarget(Joiner.on(',').join(eppInput.getTargetIds()));
return getErrorResponse(clock, Result.create(Code.CommandFailed), trid); }
EppOutput output = runFlowConvertEppErrors(flowComponentBuilder
.flowModule(new FlowModule.Builder()
.setSessionMetadata(sessionMetadata)
.setCredentials(credentials)
.setEppRequestSource(eppRequestSource)
.setIsDryRun(isDryRun)
.setIsSuperuser(isSuperuser)
.setInputXmlBytes(inputXmlBytes)
.setEppInput(eppInput)
.build())
.build());
if (output.isResponse()) {
metrics.setEppStatus(output.getResponse().getResult().getCode());
}
return output;
} finally { } finally {
metrics.export(); metrics.export();
} }
} }
/** Create a response indicating an Epp failure. */ /** Run an EPP flow and convert known exceptions into EPP error responses. */
private EppOutput runFlowConvertEppErrors(FlowComponent flowComponent) {
try {
return flowComponent.flowRunner().run();
} catch (EppException | EppExceptionInProviderException e) {
// The command failed. Send the client an error message.
EppException eppEx = (EppException) (e instanceof EppException ? e : e.getCause());
return getErrorResponse(clock, eppEx.getResult(), flowComponent.trid());
} catch (Throwable e) {
// Something bad and unexpected happened. Send the client a generic error, and log it.
logger.severe(e, "Unexpected failure");
return getErrorResponse(clock, Result.create(Code.CommandFailed), flowComponent.trid());
}
}
/** Create a response indicating an EPP failure. */
@VisibleForTesting @VisibleForTesting
static EppOutput getErrorResponse(Clock clock, Result result, Trid trid) { static EppOutput getErrorResponse(Clock clock, Result result, Trid trid) {
// Create TRID (without a clTRID) if one hasn't been created yet, as it's necessary to construct
// a valid response. This can happen if the error occurred before we could even parse out the
// clTRID (e.g. if a syntax error occurred parsing the supplied XML).
return EppOutput.create(new EppResponse.Builder() return EppOutput.create(new EppResponse.Builder()
.setTrid(trid == null ? Trid.create(null) : trid)
.setResult(result) .setResult(result)
.setTrid(trid)
.setExecutionTime(clock.nowUtc()) .setExecutionTime(clock.nowUtc())
.build()); .build());
} }

View file

@ -0,0 +1,35 @@
// Copyright 2016 The Domain Registry 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.flows;
import dagger.Subcomponent;
import google.registry.model.eppcommon.Trid;
/** Dagger component for flow classes. */
@FlowScope
@Subcomponent(modules = FlowModule.class)
public interface FlowComponent {
Trid trid();
FlowRunner flowRunner();
/** Dagger-implemented builder for this subcomponent. */
@Subcomponent.Builder
interface Builder {
Builder flowModule(FlowModule flowModule);
FlowComponent build();
}
}

View file

@ -0,0 +1,335 @@
// Copyright 2016 The Domain Registry 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.flows;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableMap;
import dagger.Module;
import dagger.Provides;
import google.registry.flows.contact.ContactCheckFlow;
import google.registry.flows.contact.ContactCreateFlow;
import google.registry.flows.contact.ContactDeleteFlow;
import google.registry.flows.contact.ContactInfoFlow;
import google.registry.flows.contact.ContactTransferApproveFlow;
import google.registry.flows.contact.ContactTransferCancelFlow;
import google.registry.flows.contact.ContactTransferQueryFlow;
import google.registry.flows.contact.ContactTransferRejectFlow;
import google.registry.flows.contact.ContactTransferRequestFlow;
import google.registry.flows.contact.ContactUpdateFlow;
import google.registry.flows.domain.ClaimsCheckFlow;
import google.registry.flows.domain.DomainAllocateFlow;
import google.registry.flows.domain.DomainApplicationCreateFlow;
import google.registry.flows.domain.DomainApplicationDeleteFlow;
import google.registry.flows.domain.DomainApplicationInfoFlow;
import google.registry.flows.domain.DomainApplicationUpdateFlow;
import google.registry.flows.domain.DomainCheckFlow;
import google.registry.flows.domain.DomainCreateFlow;
import google.registry.flows.domain.DomainDeleteFlow;
import google.registry.flows.domain.DomainInfoFlow;
import google.registry.flows.domain.DomainRenewFlow;
import google.registry.flows.domain.DomainRestoreRequestFlow;
import google.registry.flows.domain.DomainTransferApproveFlow;
import google.registry.flows.domain.DomainTransferCancelFlow;
import google.registry.flows.domain.DomainTransferQueryFlow;
import google.registry.flows.domain.DomainTransferRejectFlow;
import google.registry.flows.domain.DomainTransferRequestFlow;
import google.registry.flows.domain.DomainUpdateFlow;
import google.registry.flows.host.HostCheckFlow;
import google.registry.flows.host.HostCreateFlow;
import google.registry.flows.host.HostDeleteFlow;
import google.registry.flows.host.HostInfoFlow;
import google.registry.flows.host.HostUpdateFlow;
import google.registry.flows.picker.FlowPicker;
import google.registry.flows.poll.PollAckFlow;
import google.registry.flows.poll.PollRequestFlow;
import google.registry.flows.session.HelloFlow;
import google.registry.flows.session.LoginFlow;
import google.registry.flows.session.LogoutFlow;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import java.lang.annotation.Documented;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Provider;
import javax.inject.Qualifier;
/** Module to choose and instantiate an EPP flow. */
@Module
public class FlowModule {
private EppInput eppInput;
private byte[] inputXmlBytes;
private SessionMetadata sessionMetadata;
private TransportCredentials credentials;
private boolean isDryRun;
private EppRequestSource eppRequestSource;
private boolean isSuperuser;
private FlowModule() {}
/** Builder for {@link FlowModule}. */
static class Builder {
FlowModule module = new FlowModule();
Builder setEppInput(EppInput eppInput) {
module.eppInput = eppInput;
return this;
}
Builder setInputXmlBytes(byte[] inputXmlBytes) {
module.inputXmlBytes = inputXmlBytes;
return this;
}
Builder setSessionMetadata(SessionMetadata sessionMetadata) {
module.sessionMetadata = sessionMetadata;
return this;
}
Builder setIsDryRun(boolean isDryRun) {
module.isDryRun = isDryRun;
return this;
}
Builder setIsSuperuser(boolean isSuperuser) {
module.isSuperuser = isSuperuser;
return this;
}
Builder setEppRequestSource(EppRequestSource eppRequestSource) {
module.eppRequestSource = eppRequestSource;
return this;
}
Builder setCredentials(TransportCredentials credentials) {
module.credentials = credentials;
return this;
}
FlowModule build() {
try {
checkState(module != null, "Already built");
return module;
} finally {
module = null;
}
}
}
@Provides
@FlowScope
@InputXml
byte[] provideInputXml() {
return inputXmlBytes;
}
@Provides
@FlowScope
EppInput provideEppInput() {
return eppInput;
}
@Provides
@FlowScope
SessionMetadata provideSessionMetadata() {
return sessionMetadata;
}
@Provides
@FlowScope
@DryRun
boolean provideIsDryRun() {
return isDryRun;
}
@Provides
@FlowScope
@Transactional
boolean provideIsTransactional(Class<? extends Flow> flowClass) {
return TransactionalFlow.class.isAssignableFrom(flowClass);
}
@Provides
@FlowScope
@Superuser
boolean provideIsSuperuser() {
return isSuperuser;
}
@Provides
@FlowScope
EppRequestSource provideEppRequestSource() {
return eppRequestSource;
}
@Provides
@FlowScope
TransportCredentials provideTransportCredentials() {
return credentials;
}
@Provides
@FlowScope
@Nullable
@ClientId
static String provideClientId(SessionMetadata sessionMetadata) {
return sessionMetadata.getClientId();
}
@Provides
@FlowScope
static Trid provideTrid(EppInput eppInput) {
return Trid.create(eppInput.getCommandWrapper().getClTrid());
}
/** Provides a mapping between flow classes and injected providers. */
@Provides
@FlowScope
static Map<Class<? extends Flow>, Provider<? extends Flow>> provideFlowClassMap(
Provider<ContactCheckFlow> contactCheckFlowProvider,
Provider<ContactCreateFlow> contactCreateFlowProvider,
Provider<ContactDeleteFlow> contactDeleteFlowProvider,
Provider<ContactInfoFlow> contactInfoFlowProvider,
Provider<ContactTransferApproveFlow> contactTransferApproveFlowProvider,
Provider<ContactTransferCancelFlow> contactTransferCancelFlowProvider,
Provider<ContactTransferQueryFlow> contactTransferQueryFlowProvider,
Provider<ContactTransferRejectFlow> contactTransferRejectFlowProvider,
Provider<ContactTransferRequestFlow> contactTransferRequestFlowProvider,
Provider<ContactUpdateFlow> contactUpdateFlowProvider,
Provider<ClaimsCheckFlow> claimsCheckFlowProvider,
Provider<DomainAllocateFlow> domainAllocateFlowProvider,
Provider<DomainApplicationCreateFlow> domainApplicationCreateFlowProvider,
Provider<DomainApplicationDeleteFlow> domainApplicationDeleteFlowProvider,
Provider<DomainApplicationInfoFlow> domainApplicationInfoFlowProvider,
Provider<DomainApplicationUpdateFlow> domainApplicationUpdateFlowProvider,
Provider<DomainCheckFlow> domainCheckFlowProvider,
Provider<DomainCreateFlow> domainCreateFlowProvider,
Provider<DomainDeleteFlow> domainDeleteFlowProvider,
Provider<DomainInfoFlow> domainInfoFlowProvider,
Provider<DomainRenewFlow> domainRenewFlowProvider,
Provider<DomainRestoreRequestFlow> domainRestoreRequestFlowProvider,
Provider<DomainTransferApproveFlow> domainTransferApproveFlowProvider,
Provider<DomainTransferCancelFlow> domainTransferCancelFlowProvider,
Provider<DomainTransferQueryFlow> domainTransferQueryFlowProvider,
Provider<DomainTransferRejectFlow> domainTransferRejectFlowProvider,
Provider<DomainTransferRequestFlow> domainTransferRequestFlowProvider,
Provider<DomainUpdateFlow> domainUpdateFlowProvider,
Provider<HostCheckFlow> hostCheckFlowProvider,
Provider<HostCreateFlow> hostCreateFlowProvider,
Provider<HostDeleteFlow> hostDeleteFlowProvider,
Provider<HostInfoFlow> hostInfoFlowProvider,
Provider<HostUpdateFlow> hostUpdateFlowProvider,
Provider<PollAckFlow> pollAckFlowProvider,
Provider<PollRequestFlow> pollRequestFlowProvider,
Provider<HelloFlow> helloFlowProvider,
Provider<LoginFlow> loginFlowProvider,
Provider<LogoutFlow> logoutFlowProvider) {
return new ImmutableMap.Builder<Class<? extends Flow>, Provider<? extends Flow>>()
.put(ContactCheckFlow.class, contactCheckFlowProvider)
.put(ContactCreateFlow.class, contactCreateFlowProvider)
.put(ContactDeleteFlow.class, contactDeleteFlowProvider)
.put(ContactInfoFlow.class, contactInfoFlowProvider)
.put(ContactTransferApproveFlow.class, contactTransferApproveFlowProvider)
.put(ContactTransferCancelFlow.class, contactTransferCancelFlowProvider)
.put(ContactTransferQueryFlow.class, contactTransferQueryFlowProvider)
.put(ContactTransferRejectFlow.class, contactTransferRejectFlowProvider)
.put(ContactTransferRequestFlow.class, contactTransferRequestFlowProvider)
.put(ContactUpdateFlow.class, contactUpdateFlowProvider)
.put(ClaimsCheckFlow.class, claimsCheckFlowProvider)
.put(DomainAllocateFlow.class, domainAllocateFlowProvider)
.put(DomainApplicationCreateFlow.class, domainApplicationCreateFlowProvider)
.put(DomainApplicationDeleteFlow.class, domainApplicationDeleteFlowProvider)
.put(DomainApplicationInfoFlow.class, domainApplicationInfoFlowProvider)
.put(DomainApplicationUpdateFlow.class, domainApplicationUpdateFlowProvider)
.put(DomainCheckFlow.class, domainCheckFlowProvider)
.put(DomainCreateFlow.class, domainCreateFlowProvider)
.put(DomainDeleteFlow.class, domainDeleteFlowProvider)
.put(DomainInfoFlow.class, domainInfoFlowProvider)
.put(DomainRenewFlow.class, domainRenewFlowProvider)
.put(DomainRestoreRequestFlow.class, domainRestoreRequestFlowProvider)
.put(DomainTransferApproveFlow.class, domainTransferApproveFlowProvider)
.put(DomainTransferCancelFlow.class, domainTransferCancelFlowProvider)
.put(DomainTransferQueryFlow.class, domainTransferQueryFlowProvider)
.put(DomainTransferRejectFlow.class, domainTransferRejectFlowProvider)
.put(DomainTransferRequestFlow.class, domainTransferRequestFlowProvider)
.put(DomainUpdateFlow.class, domainUpdateFlowProvider)
.put(HostCheckFlow.class, hostCheckFlowProvider)
.put(HostCreateFlow.class, hostCreateFlowProvider)
.put(HostDeleteFlow.class, hostDeleteFlowProvider)
.put(HostInfoFlow.class, hostInfoFlowProvider)
.put(HostUpdateFlow.class, hostUpdateFlowProvider)
.put(PollAckFlow.class, pollAckFlowProvider)
.put(PollRequestFlow.class, pollRequestFlowProvider)
.put(HelloFlow.class, helloFlowProvider)
.put(LoginFlow.class, loginFlowProvider)
.put(LogoutFlow.class, logoutFlowProvider)
.build();
}
@Provides
@FlowScope
static Class<? extends Flow> provideFlowClass(EppInput eppInput) {
try {
return FlowPicker.getFlowClass(eppInput);
} catch (EppException e) {
throw new EppExceptionInProviderException(e);
}
}
@Provides
@FlowScope
static Flow provideFlow(
Map<Class<? extends Flow>, Provider<? extends Flow>> flowProviders,
Class<? extends Flow> flowClass) {
return flowProviders.get(flowClass).get();
}
/** Wrapper class to carry an {@link EppException} to the calling code. */
static class EppExceptionInProviderException extends RuntimeException {
EppExceptionInProviderException(EppException exception) {
super(exception);
}
}
/** Dagger qualifier for inputXml. */
@Qualifier
@Documented
public @interface InputXml {}
/** Dagger qualifier for registrar client id. */
@Qualifier
@Documented
public @interface ClientId {}
/** Dagger qualifier for whether a flow is in dry run mode. */
@Qualifier
@Documented
public @interface DryRun {}
/** Dagger qualifier for whether a flow is in superuser mode. */
@Qualifier
@Documented
public @interface Superuser {}
/** Dagger qualifier for whether a flow is transactional. */
@Qualifier
@Documented
public @interface Transactional {}
}

View file

@ -23,16 +23,24 @@ import com.google.common.base.Strings;
import com.googlecode.objectify.Work; import com.googlecode.objectify.Work;
import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.DryRun;
import google.registry.flows.FlowModule.InputXml;
import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.Transactional;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
import google.registry.monitoring.whitebox.EppMetrics; import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.util.Clock; import google.registry.util.Clock;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import google.registry.util.TypeUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
/** 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. */
public class FlowRunner { public class FlowRunner {
@ -40,43 +48,20 @@ public class FlowRunner {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
private final Class<? extends Flow> flowClass; @Inject @Nullable @ClientId String clientId;
private final EppInput eppInput; @Inject Clock clock;
private final Trid trid; @Inject TransportCredentials credentials;
private final SessionMetadata sessionMetadata; @Inject EppInput eppInput;
private final TransportCredentials credentials; @Inject EppRequestSource eppRequestSource;
private final EppRequestSource eppRequestSource; @Inject Provider<Flow> flowProvider;
private final boolean isDryRun; @Inject @InputXml byte[] inputXmlBytes;
private final boolean isSuperuser; @Inject @DryRun boolean isDryRun;
private final byte[] inputXmlBytes; @Inject @Superuser boolean isSuperuser;
private final EppMetrics metrics; @Inject @Transactional boolean isTransactional;
private final Clock clock; @Inject EppMetrics metrics;
@Inject SessionMetadata sessionMetadata;
@Inject Trid trid;
public FlowRunner( @Inject FlowRunner() {}
Class<? extends Flow> flowClass,
EppInput eppInput,
Trid trid,
SessionMetadata sessionMetadata,
TransportCredentials credentials,
EppRequestSource eppRequestSource,
boolean isDryRun,
boolean isSuperuser,
byte[] inputXmlBytes,
final EppMetrics metrics,
Clock clock) {
this.flowClass = flowClass;
this.eppInput = eppInput;
this.trid = trid;
this.sessionMetadata = sessionMetadata;
this.credentials = credentials;
this.eppRequestSource = eppRequestSource;
this.isDryRun = isDryRun;
this.isSuperuser = isSuperuser;
this.inputXmlBytes = inputXmlBytes;
this.metrics = metrics;
this.clock = clock;
}
public EppOutput run() throws EppException { public EppOutput run() throws EppException {
String clientId = sessionMetadata.getClientId(); String clientId = sessionMetadata.getClientId();
@ -90,10 +75,8 @@ public class FlowRunner {
eppRequestSource, eppRequestSource,
isDryRun ? "DRY_RUN" : "LIVE", isDryRun ? "DRY_RUN" : "LIVE",
isSuperuser ? "SUPERUSER" : "NORMAL"); isSuperuser ? "SUPERUSER" : "NORMAL");
if (!isTransactional()) { if (!isTransactional) {
if (metrics != null) {
metrics.incrementAttempts(); metrics.incrementAttempts();
}
return createAndInitFlow(clock.nowUtc()).run(); return createAndInitFlow(clock.nowUtc()).run();
} }
// We log the command in a structured format. Note that we do this before the transaction; // We log the command in a structured format. Note that we do this before the transaction;
@ -107,9 +90,7 @@ public class FlowRunner {
EppOutput flowResult = ofy().transact(new Work<EppOutput>() { EppOutput flowResult = ofy().transact(new Work<EppOutput>() {
@Override @Override
public EppOutput run() { public EppOutput run() {
if (metrics != null) {
metrics.incrementAttempts(); metrics.incrementAttempts();
}
try { try {
EppOutput output = createAndInitFlow(ofy().getTransactionTime()).run(); EppOutput output = createAndInitFlow(ofy().getTransactionTime()).run();
if (isDryRun) { if (isDryRun) {
@ -137,7 +118,7 @@ public class FlowRunner {
} }
private Flow createAndInitFlow(DateTime now) throws EppException { private Flow createAndInitFlow(DateTime now) throws EppException {
return TypeUtils.<Flow>instantiate(flowClass).init( return flowProvider.get().init(
eppInput, eppInput,
trid, trid,
sessionMetadata, sessionMetadata,
@ -148,10 +129,6 @@ public class FlowRunner {
inputXmlBytes); inputXmlBytes);
} }
public boolean isTransactional() {
return TransactionalFlow.class.isAssignableFrom(flowClass);
}
/** /**
* Helper for logging in json format. * Helper for logging in json format.
* *

View file

@ -0,0 +1,35 @@
// Copyright 2016 The Domain Registry 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.flows;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Scope;
/**
* Dagger annotation for flow-scoped components.
*
* <p>Note that this scope survives across transactional retries of a flow. That is, it is scoped to
* the overall execution of a flow, and not to a specific attempt.
*/
@Scope
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FlowScope {}

View file

@ -27,12 +27,17 @@ import google.registry.model.eppoutput.CheckData.ContactCheckData;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that checks whether a contact can be provisioned. * An EPP flow that checks whether a contact can be provisioned.
* *
* @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException} * @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException}
*/ */
public class ContactCheckFlow extends ResourceCheckFlow<ContactResource, Check> { public class ContactCheckFlow extends ResourceCheckFlow<ContactResource, Check> {
@Inject ContactCheckFlow() {}
@Override @Override
protected CheckData getCheckData() { protected CheckData getCheckData() {
Set<String> existingIds = checkResourcesExist(resourceClass, targetIds, now); Set<String> existingIds = checkResourcesExist(resourceClass, targetIds, now);

View file

@ -29,6 +29,8 @@ import google.registry.model.eppoutput.EppOutput;
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;
/** /**
* An EPP flow that creates a new contact resource. * An EPP flow that creates a new contact resource.
* *
@ -37,6 +39,9 @@ import google.registry.model.reporting.HistoryEntry;
* @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException}
*/ */
public class ContactCreateFlow extends ResourceCreateFlow<ContactResource, Builder, Create> { public class ContactCreateFlow extends ResourceCreateFlow<ContactResource, Builder, Create> {
@Inject ContactCreateFlow() {}
@Override @Override
protected EppOutput getOutput() { protected EppOutput getOutput() {
return createOutput(Success, ContactCreateData.create(newResource.getContactId(), now)); return createOutput(Success, ContactCreateData.create(newResource.getContactId(), now));

View file

@ -36,6 +36,8 @@ import google.registry.model.contact.ContactResource.Builder;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that deletes a contact resource. * An EPP flow that deletes a contact resource.
* *
@ -49,6 +51,8 @@ public class ContactDeleteFlow extends ResourceAsyncDeleteFlow<ContactResource,
/** In {@link #isLinkedForFailfast}, check this (arbitrary) number of resources from the query. */ /** In {@link #isLinkedForFailfast}, check this (arbitrary) number of resources from the query. */
private static final int FAILFAST_CHECK_COUNT = 5; private static final int FAILFAST_CHECK_COUNT = 5;
@Inject ContactDeleteFlow() {}
@Override @Override
protected boolean isLinkedForFailfast(final Ref<ContactResource> ref) { protected boolean isLinkedForFailfast(final Ref<ContactResource> ref) {
// Query for the first few linked domains, and if found, actually load them. The query is // Query for the first few linked domains, and if found, actually load them. The query is

View file

@ -18,10 +18,14 @@ import google.registry.flows.ResourceInfoFlow;
import google.registry.model.contact.ContactCommand.Info; import google.registry.model.contact.ContactCommand.Info;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import javax.inject.Inject;
/** /**
* An EPP flow that reads a contact. * An EPP flow that reads a contact.
* *
* @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException} * @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException}
*/ */
public class ContactInfoFlow extends ResourceInfoFlow<ContactResource, Info> {} public class ContactInfoFlow extends ResourceInfoFlow<ContactResource, Info> {
@Inject ContactInfoFlow() {}
}

View file

@ -20,6 +20,8 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.contact.ContactResource.Builder; import google.registry.model.contact.ContactResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that approves a pending transfer on a {@link ContactResource}. * An EPP flow that approves a pending transfer on a {@link ContactResource}.
* *
@ -30,6 +32,9 @@ import google.registry.model.reporting.HistoryEntry;
*/ */
public class ContactTransferApproveFlow public class ContactTransferApproveFlow
extends ResourceTransferApproveFlow<ContactResource, Builder, Transfer> { extends ResourceTransferApproveFlow<ContactResource, Builder, Transfer> {
@Inject ContactTransferApproveFlow() {}
@Override @Override
protected final HistoryEntry.Type getHistoryEntryType() { protected final HistoryEntry.Type getHistoryEntryType() {
return HistoryEntry.Type.CONTACT_TRANSFER_APPROVE; return HistoryEntry.Type.CONTACT_TRANSFER_APPROVE;

View file

@ -20,6 +20,8 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.contact.ContactResource.Builder; import google.registry.model.contact.ContactResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that cancels a pending transfer on a {@link ContactResource}. * An EPP flow that cancels a pending transfer on a {@link ContactResource}.
* *
@ -30,6 +32,9 @@ import google.registry.model.reporting.HistoryEntry;
*/ */
public class ContactTransferCancelFlow public class ContactTransferCancelFlow
extends ResourceTransferCancelFlow<ContactResource, Builder, Transfer> { extends ResourceTransferCancelFlow<ContactResource, Builder, Transfer> {
@Inject ContactTransferCancelFlow() {}
@Override @Override
protected final HistoryEntry.Type getHistoryEntryType() { protected final HistoryEntry.Type getHistoryEntryType() {
return HistoryEntry.Type.CONTACT_TRANSFER_CANCEL; return HistoryEntry.Type.CONTACT_TRANSFER_CANCEL;

View file

@ -18,6 +18,8 @@ import google.registry.flows.ResourceTransferQueryFlow;
import google.registry.model.contact.ContactCommand.Transfer; import google.registry.model.contact.ContactCommand.Transfer;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import javax.inject.Inject;
/** /**
* An EPP flow that queries a pending transfer on a {@link ContactResource}. * An EPP flow that queries a pending transfer on a {@link ContactResource}.
* *
@ -27,4 +29,5 @@ import google.registry.model.contact.ContactResource;
* @error {@link google.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException} * @error {@link google.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException}
*/ */
public class ContactTransferQueryFlow extends ResourceTransferQueryFlow<ContactResource, Transfer> { public class ContactTransferQueryFlow extends ResourceTransferQueryFlow<ContactResource, Transfer> {
@Inject ContactTransferQueryFlow() {}
} }

View file

@ -20,6 +20,8 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.contact.ContactResource.Builder; import google.registry.model.contact.ContactResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that rejects a pending transfer on a {@link ContactResource}. * An EPP flow that rejects a pending transfer on a {@link ContactResource}.
* *
@ -30,6 +32,9 @@ import google.registry.model.reporting.HistoryEntry;
*/ */
public class ContactTransferRejectFlow public class ContactTransferRejectFlow
extends ResourceTransferRejectFlow<ContactResource, Builder, Transfer> { extends ResourceTransferRejectFlow<ContactResource, Builder, Transfer> {
@Inject ContactTransferRejectFlow() {}
@Override @Override
protected final HistoryEntry.Type getHistoryEntryType() { protected final HistoryEntry.Type getHistoryEntryType() {
return HistoryEntry.Type.CONTACT_TRANSFER_REJECT; return HistoryEntry.Type.CONTACT_TRANSFER_REJECT;

View file

@ -22,6 +22,8 @@ import google.registry.model.reporting.HistoryEntry;
import org.joda.time.Duration; import org.joda.time.Duration;
import javax.inject.Inject;
/** /**
* An EPP flow that requests a transfer on a {@link ContactResource}. * An EPP flow that requests a transfer on a {@link ContactResource}.
* *
@ -34,6 +36,8 @@ import org.joda.time.Duration;
public class ContactTransferRequestFlow public class ContactTransferRequestFlow
extends ResourceTransferRequestFlow<ContactResource, Transfer> { extends ResourceTransferRequestFlow<ContactResource, Transfer> {
@Inject ContactTransferRequestFlow() {}
@Override @Override
protected final HistoryEntry.Type getHistoryEntryType() { protected final HistoryEntry.Type getHistoryEntryType() {
return HistoryEntry.Type.CONTACT_TRANSFER_REQUEST; return HistoryEntry.Type.CONTACT_TRANSFER_REQUEST;

View file

@ -24,6 +24,8 @@ import google.registry.model.contact.ContactResource;
import google.registry.model.contact.ContactResource.Builder; import google.registry.model.contact.ContactResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that updates a contact resource. * An EPP flow that updates a contact resource.
* *
@ -36,6 +38,9 @@ import google.registry.model.reporting.HistoryEntry;
* @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException} * @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException}
*/ */
public class ContactUpdateFlow extends ResourceUpdateFlow<ContactResource, Builder, Update> { public class ContactUpdateFlow extends ResourceUpdateFlow<ContactResource, Builder, Update> {
@Inject ContactUpdateFlow() {}
@Override @Override
protected void verifyNewUpdatedStateIsAllowed() throws EppException { protected void verifyNewUpdatedStateIsAllowed() throws EppException {
validateAsciiPostalInfo(newResource.getInternationalizedPostalInfo()); validateAsciiPostalInfo(newResource.getInternationalizedPostalInfo());

View file

@ -35,6 +35,8 @@ import google.registry.model.tmch.ClaimsListShard;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.inject.Inject;
/** /**
* An EPP flow that checks whether strings are trademarked. * An EPP flow that checks whether strings are trademarked.
* *
@ -48,6 +50,8 @@ public class ClaimsCheckFlow extends BaseDomainCheckFlow {
public static final ImmutableSet<TldState> DISALLOWED_TLD_STATES = Sets.immutableEnumSet( public static final ImmutableSet<TldState> DISALLOWED_TLD_STATES = Sets.immutableEnumSet(
TldState.PREDELEGATION, TldState.SUNRISE); TldState.PREDELEGATION, TldState.SUNRISE);
@Inject ClaimsCheckFlow() {}
@Override @Override
protected void initDomainCheckFlow() throws EppException { protected void initDomainCheckFlow() throws EppException {
registerExtensions(LaunchCheckExtension.class); registerExtensions(LaunchCheckExtension.class);

View file

@ -48,6 +48,8 @@ import google.registry.model.registry.label.ReservationType;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.tmch.LordnTask; import google.registry.tmch.LordnTask;
import javax.inject.Inject;
/** /**
* An EPP flow that allocates a new domain resource from a domain application. * An EPP flow that allocates a new domain resource from a domain application.
* *
@ -62,6 +64,8 @@ public class DomainAllocateFlow extends DomainCreateOrAllocateFlow {
protected AllocateCreateExtension allocateCreate; protected AllocateCreateExtension allocateCreate;
protected DomainApplication application; protected DomainApplication application;
@Inject DomainAllocateFlow() {}
@Override @Override
protected final void initDomainCreateOrAllocateFlow() { protected final void initDomainCreateOrAllocateFlow() {
registerExtensions(AllocateCreateExtension.class); registerExtensions(AllocateCreateExtension.class);

View file

@ -51,6 +51,8 @@ import google.registry.model.smd.EncodedSignedMark;
import java.util.List; import java.util.List;
import javax.inject.Inject;
/** /**
* An EPP flow that creates a new application for a domain resource. * An EPP flow that creates a new application for a domain resource.
* *
@ -114,6 +116,8 @@ import java.util.List;
*/ */
public class DomainApplicationCreateFlow extends BaseDomainCreateFlow<DomainApplication, Builder> { public class DomainApplicationCreateFlow extends BaseDomainCreateFlow<DomainApplication, Builder> {
@Inject DomainApplicationCreateFlow() {}
@Override @Override
protected void initDomainCreateFlow() { protected void initDomainCreateFlow() {
registerExtensions(FeeCreateExtension.class, LaunchCreateExtension.class); registerExtensions(FeeCreateExtension.class, LaunchCreateExtension.class);

View file

@ -36,6 +36,8 @@ import google.registry.model.reporting.HistoryEntry;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that deletes a domain application. * An EPP flow that deletes a domain application.
* *
@ -51,6 +53,8 @@ import java.util.Set;
public class DomainApplicationDeleteFlow public class DomainApplicationDeleteFlow
extends ResourceSyncDeleteFlow<DomainApplication, Builder, Delete> { extends ResourceSyncDeleteFlow<DomainApplication, Builder, Delete> {
@Inject DomainApplicationDeleteFlow() {}
@Override @Override
protected void initResourceCreateOrMutateFlow() throws EppException { protected void initResourceCreateOrMutateFlow() throws EppException {
registerExtensions(LaunchDeleteExtension.class); registerExtensions(LaunchDeleteExtension.class);

View file

@ -32,6 +32,8 @@ 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 javax.inject.Inject;
/** /**
* An EPP flow that reads a domain application. * An EPP flow that reads a domain application.
* *
@ -45,6 +47,8 @@ public class DomainApplicationInfoFlow extends BaseDomainInfoFlow<DomainApplicat
private boolean includeMarks; private boolean includeMarks;
@Inject DomainApplicationInfoFlow() {}
@Override @Override
protected final void initSingleResourceFlow() throws EppException { protected final void initSingleResourceFlow() throws EppException {
registerExtensions(LaunchInfoExtension.class); registerExtensions(LaunchInfoExtension.class);

View file

@ -30,6 +30,8 @@ import google.registry.model.domain.secdns.SecDnsUpdateExtension;
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;
/** /**
* An EPP flow that updates a domain resource. * An EPP flow that updates a domain resource.
* *
@ -59,6 +61,8 @@ import google.registry.model.reporting.HistoryEntry;
public class DomainApplicationUpdateFlow public class DomainApplicationUpdateFlow
extends BaseDomainUpdateFlow<DomainApplication, Builder> { extends BaseDomainUpdateFlow<DomainApplication, Builder> {
@Inject DomainApplicationUpdateFlow() {}
@Override @Override
protected void initDomainUpdateFlow() throws EppException { protected void initDomainUpdateFlow() throws EppException {
registerExtensions(LaunchUpdateExtension.class, SecDnsUpdateExtension.class); registerExtensions(LaunchUpdateExtension.class, SecDnsUpdateExtension.class);

View file

@ -48,6 +48,8 @@ import google.registry.model.registry.label.ReservationType;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that checks whether a domain can be provisioned. * An EPP flow that checks whether a domain can be provisioned.
* *
@ -81,6 +83,8 @@ public class DomainCheckFlow extends BaseDomainCheckFlow {
protected RegTypeCheckExtension regTypeExtension; protected RegTypeCheckExtension regTypeExtension;
@Inject DomainCheckFlow() {}
@Override @Override
protected void initDomainCheckFlow() throws EppException { protected void initDomainCheckFlow() throws EppException {
registerExtensions( registerExtensions(

View file

@ -41,6 +41,8 @@ import google.registry.tmch.LordnTask;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that creates a new domain resource. * An EPP flow that creates a new domain resource.
* *
@ -101,6 +103,8 @@ public class DomainCreateFlow extends DomainCreateOrAllocateFlow {
protected RegTypeCreateExtension regTypeExtension; protected RegTypeCreateExtension regTypeExtension;
@Inject DomainCreateFlow() {}
private boolean isAnchorTenant() { private boolean isAnchorTenant() {
return isAnchorTenantViaReservation || isAnchorTenantViaExtension; return isAnchorTenantViaReservation || isAnchorTenantViaExtension;
} }

View file

@ -55,6 +55,8 @@ import org.joda.money.CurrencyUnit;
import org.joda.money.Money; import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import javax.inject.Inject;
/** /**
* An EPP flow that deletes a domain resource. * An EPP flow that deletes a domain resource.
* *
@ -73,6 +75,8 @@ public class DomainDeleteFlow extends ResourceSyncDeleteFlow<DomainResource, Bui
ImmutableList<Credit> credits; ImmutableList<Credit> credits;
@Inject DomainDeleteFlow() {}
@Override @Override
protected void initResourceCreateOrMutateFlow() throws EppException { protected void initResourceCreateOrMutateFlow() throws EppException {
registerExtensions(SecDnsUpdateExtension.class); registerExtensions(SecDnsUpdateExtension.class);

View file

@ -31,6 +31,8 @@ import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import java.util.List; import java.util.List;
import javax.inject.Inject;
/** /**
* An EPP flow that reads a domain. * An EPP flow that reads a domain.
* *
@ -45,6 +47,8 @@ public class DomainInfoFlow extends BaseDomainInfoFlow<DomainResource, Builder>
protected List<String> registrationTypes; protected List<String> registrationTypes;
@Inject DomainInfoFlow() {}
@Override @Override
protected void initSingleResourceFlow() throws EppException { protected void initSingleResourceFlow() throws EppException {
registerExtensions(FeeInfoExtension.class); registerExtensions(FeeInfoExtension.class);

View file

@ -59,6 +59,8 @@ import org.joda.time.DateTime;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that updates a domain resource. * An EPP flow that updates a domain resource.
* *
@ -86,6 +88,8 @@ public class DomainRenewFlow extends OwnedResourceMutateFlow<DomainResource, Ren
protected FeeRenewExtension feeRenew; protected FeeRenewExtension feeRenew;
protected Money renewCost; protected Money renewCost;
@Inject DomainRenewFlow() {}
@Override @Override
protected Set<StatusValue> getDisallowedStatuses() { protected Set<StatusValue> getDisallowedStatuses() {
return RENEW_DISALLOWED_STATUSES; return RENEW_DISALLOWED_STATUSES;

View file

@ -54,6 +54,8 @@ import google.registry.model.reporting.HistoryEntry;
import org.joda.money.Money; import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import javax.inject.Inject;
/** /**
* An EPP flow that requests that a deleted domain be restored. * An EPP flow that requests that a deleted domain be restored.
* *
@ -77,6 +79,8 @@ public class DomainRestoreRequestFlow extends OwnedResourceMutateFlow<DomainReso
protected Money restoreCost; protected Money restoreCost;
protected Money renewCost; protected Money renewCost;
@Inject DomainRestoreRequestFlow() {}
@Override @Override
protected final void initResourceCreateOrMutateFlow() throws EppException { protected final void initResourceCreateOrMutateFlow() throws EppException {
registerExtensions(FeeUpdateExtension.class, RgpUpdateExtension.class); registerExtensions(FeeUpdateExtension.class, RgpUpdateExtension.class);

View file

@ -45,6 +45,8 @@ import google.registry.model.transfer.TransferData;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import javax.inject.Inject;
/** /**
* An EPP flow that approves a pending transfer on a {@link DomainResource}. * An EPP flow that approves a pending transfer on a {@link DomainResource}.
* *
@ -60,6 +62,8 @@ import org.joda.time.DateTime;
public class DomainTransferApproveFlow extends public class DomainTransferApproveFlow extends
ResourceTransferApproveFlow<DomainResource, Builder, Transfer> { ResourceTransferApproveFlow<DomainResource, Builder, Transfer> {
@Inject DomainTransferApproveFlow() {}
@Override @Override
protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException { protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException {
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld()); checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());

View file

@ -25,6 +25,8 @@ import google.registry.model.domain.DomainResource;
import google.registry.model.domain.DomainResource.Builder; import google.registry.model.domain.DomainResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that cancels a pending transfer on a {@link DomainResource}. * An EPP flow that cancels a pending transfer on a {@link DomainResource}.
* *
@ -37,6 +39,8 @@ import google.registry.model.reporting.HistoryEntry;
public class DomainTransferCancelFlow public class DomainTransferCancelFlow
extends ResourceTransferCancelFlow<DomainResource, Builder, Transfer> { extends ResourceTransferCancelFlow<DomainResource, Builder, Transfer> {
@Inject DomainTransferCancelFlow() {}
/** /**
* Reopen the autorenew event and poll message that we closed for the implicit transfer. * Reopen the autorenew event and poll message that we closed for the implicit transfer.
* This may end up recreating the autorenew poll message if it was deleted when the transfer * This may end up recreating the autorenew poll message if it was deleted when the transfer

View file

@ -18,6 +18,8 @@ import google.registry.flows.ResourceTransferQueryFlow;
import google.registry.model.domain.DomainCommand.Transfer; import google.registry.model.domain.DomainCommand.Transfer;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import javax.inject.Inject;
/** /**
* An EPP flow that queries a pending transfer on a {@link DomainResource}. * An EPP flow that queries a pending transfer on a {@link DomainResource}.
* *
@ -26,4 +28,6 @@ import google.registry.model.domain.DomainResource;
* @error {@link google.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException} * @error {@link google.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException}
* @error {@link google.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException} * @error {@link google.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException}
*/ */
public class DomainTransferQueryFlow extends ResourceTransferQueryFlow<DomainResource, Transfer> {} public class DomainTransferQueryFlow extends ResourceTransferQueryFlow<DomainResource, Transfer> {
@Inject DomainTransferQueryFlow() {}
}

View file

@ -25,6 +25,8 @@ import google.registry.model.domain.DomainResource;
import google.registry.model.domain.DomainResource.Builder; import google.registry.model.domain.DomainResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that rejects a pending transfer on a {@link DomainResource}. * An EPP flow that rejects a pending transfer on a {@link DomainResource}.
* *
@ -37,6 +39,8 @@ import google.registry.model.reporting.HistoryEntry;
public class DomainTransferRejectFlow public class DomainTransferRejectFlow
extends ResourceTransferRejectFlow<DomainResource, Builder, Transfer> { extends ResourceTransferRejectFlow<DomainResource, Builder, Transfer> {
@Inject DomainTransferRejectFlow() {}
@Override @Override
protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException { protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException {
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld()); checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());

View file

@ -56,6 +56,8 @@ import org.joda.time.Duration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that requests a transfer on a {@link DomainResource}. * An EPP flow that requests a transfer on a {@link DomainResource}.
* *
@ -97,6 +99,8 @@ public class DomainTransferRequestFlow
*/ */
private FeeTransferExtension feeTransfer; private FeeTransferExtension feeTransfer;
@Inject DomainTransferRequestFlow() {}
@Override @Override
protected Duration getAutomaticTransferLength() { protected Duration getAutomaticTransferLength() {
return Registry.get(existingResource.getTld()).getAutomaticTransferLength(); return Registry.get(existingResource.getTld()).getAutomaticTransferLength();

View file

@ -39,6 +39,8 @@ import org.joda.time.DateTime;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that updates a domain resource. * An EPP flow that updates a domain resource.
* *
@ -70,6 +72,8 @@ public class DomainUpdateFlow extends BaseDomainUpdateFlow<DomainResource, Build
protected RegTypeUpdateExtension regTypeExtension; protected RegTypeUpdateExtension regTypeExtension;
@Inject DomainUpdateFlow() {}
@Override @Override
protected void initDomainUpdateFlow() { protected void initDomainUpdateFlow() {
registerExtensions(SecDnsUpdateExtension.class, RegTypeUpdateExtension.class); registerExtensions(SecDnsUpdateExtension.class, RegTypeUpdateExtension.class);

View file

@ -27,12 +27,17 @@ import google.registry.model.host.HostResource;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow that checks whether a host can be provisioned. * An EPP flow that checks whether a host can be provisioned.
* *
* @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException} * @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException}
*/ */
public class HostCheckFlow extends ResourceCheckFlow<HostResource, Check> { public class HostCheckFlow extends ResourceCheckFlow<HostResource, Check> {
@Inject HostCheckFlow() {}
@Override @Override
protected CheckData getCheckData() { protected CheckData getCheckData() {
Set<String> existingIds = checkResourcesExist(resourceClass, targetIds, now); Set<String> existingIds = checkResourcesExist(resourceClass, targetIds, now);

View file

@ -40,6 +40,8 @@ import google.registry.model.host.HostResource.Builder;
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;
/** /**
* An EPP flow that creates a new host resource. * An EPP flow that creates a new host resource.
* *
@ -67,6 +69,8 @@ public class HostCreateFlow extends ResourceCreateFlow<HostResource, Builder, Cr
*/ */
private Optional<Ref<DomainResource>> superordinateDomain; private Optional<Ref<DomainResource>> superordinateDomain;
@Inject HostCreateFlow() {}
@Override @Override
protected void initResourceCreateOrMutateFlow() throws EppException { protected void initResourceCreateOrMutateFlow() throws EppException {
superordinateDomain = Optional.fromNullable(lookupSuperordinateDomain( superordinateDomain = Optional.fromNullable(lookupSuperordinateDomain(

View file

@ -36,6 +36,8 @@ import google.registry.model.host.HostResource;
import google.registry.model.host.HostResource.Builder; import google.registry.model.host.HostResource.Builder;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import javax.inject.Inject;
/** /**
* An EPP flow that deletes a host resource. * An EPP flow that deletes a host resource.
* *
@ -49,6 +51,8 @@ public class HostDeleteFlow extends ResourceAsyncDeleteFlow<HostResource, Builde
/** In {@link #isLinkedForFailfast}, check this (arbitrary) number of resources from the query. */ /** In {@link #isLinkedForFailfast}, check this (arbitrary) number of resources from the query. */
private static final int FAILFAST_CHECK_COUNT = 5; private static final int FAILFAST_CHECK_COUNT = 5;
@Inject HostDeleteFlow() {}
@Override @Override
protected boolean isLinkedForFailfast(final Ref<HostResource> ref) { protected boolean isLinkedForFailfast(final Ref<HostResource> ref) {
// Query for the first few linked domains, and if found, actually load them. The query is // Query for the first few linked domains, and if found, actually load them. The query is

View file

@ -18,9 +18,13 @@ import google.registry.flows.ResourceInfoFlow;
import google.registry.model.host.HostCommand; import google.registry.model.host.HostCommand;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import javax.inject.Inject;
/** /**
* An EPP flow that reads a host. * An EPP flow that reads a host.
* *
* @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException} * @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException}
*/ */
public class HostInfoFlow extends ResourceInfoFlow<HostResource, HostCommand.Info> {} public class HostInfoFlow extends ResourceInfoFlow<HostResource, HostCommand.Info> {
@Inject HostInfoFlow() {}
}

View file

@ -47,6 +47,8 @@ import org.joda.time.Duration;
import java.util.Objects; import java.util.Objects;
import javax.inject.Inject;
/** /**
* An EPP flow that updates a host resource. * An EPP flow that updates a host resource.
* *
@ -72,6 +74,8 @@ public class HostUpdateFlow extends ResourceUpdateFlow<HostResource, Builder, Up
private String newHostName; private String newHostName;
private boolean isHostRename; private boolean isHostRename;
@Inject HostUpdateFlow() {}
@Override @Override
protected void initResourceCreateOrMutateFlow() throws EppException { protected void initResourceCreateOrMutateFlow() throws EppException {
String suppliedNewHostName = command.getInnerChange().getFullyQualifiedHostName(); String suppliedNewHostName = command.getInnerChange().getFullyQualifiedHostName();

View file

@ -35,6 +35,8 @@ import google.registry.model.poll.PollMessageExternalKeyConverter.PollMessageExt
import org.joda.time.DateTime; import org.joda.time.DateTime;
import javax.inject.Inject;
/** /**
* An EPP flow for acknowledging poll messages. * An EPP flow for acknowledging poll messages.
* *
@ -45,6 +47,8 @@ import org.joda.time.DateTime;
*/ */
public class PollAckFlow extends PollFlow implements TransactionalFlow { public class PollAckFlow extends PollFlow implements TransactionalFlow {
@Inject PollAckFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppOutput run() throws EppException {
if (command.getMessageId() == null) { if (command.getMessageId() == null) {

View file

@ -29,6 +29,8 @@ import google.registry.model.poll.PollMessage;
import java.util.List; import java.util.List;
import javax.inject.Inject;
/** /**
* An EPP flow for requesting poll messages. * An EPP flow for requesting poll messages.
* *
@ -36,6 +38,8 @@ import java.util.List;
*/ */
public class PollRequestFlow extends PollFlow { public class PollRequestFlow extends PollFlow {
@Inject PollRequestFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppOutput run() throws EppException {
if (command.getMessageId() != null) { if (command.getMessageId() != null) {

View file

@ -18,8 +18,13 @@ import google.registry.flows.Flow;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
import google.registry.model.eppoutput.Greeting; import google.registry.model.eppoutput.Greeting;
import javax.inject.Inject;
/** A flow for an Epp "hello". */ /** A flow for an Epp "hello". */
public class HelloFlow extends Flow { public class HelloFlow extends Flow {
@Inject HelloFlow() {}
@Override @Override
public EppOutput run() { public EppOutput run() {
return EppOutput.create(Greeting.create(now)); return EppOutput.create(Greeting.create(now));

View file

@ -41,6 +41,8 @@ import google.registry.util.FormattingLogger;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
/** /**
* An EPP flow for login. * An EPP flow for login.
* *
@ -68,6 +70,8 @@ public class LoginFlow extends Flow {
/** Maximum number of failed login attempts allowed per connection. */ /** Maximum number of failed login attempts allowed per connection. */
private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3; private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3;
@Inject LoginFlow() {}
/** Run the flow and log errors. */ /** Run the flow and log errors. */
@Override @Override
public final EppOutput run() throws EppException { public final EppOutput run() throws EppException {

View file

@ -20,12 +20,17 @@ import google.registry.flows.EppException;
import google.registry.flows.LoggedInFlow; import google.registry.flows.LoggedInFlow;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
import javax.inject.Inject;
/** /**
* An EPP flow for logout. * An EPP flow for logout.
* *
* @error {@link google.registry.flows.LoggedInFlow.NotLoggedInException} * @error {@link google.registry.flows.LoggedInFlow.NotLoggedInException}
*/ */
public class LogoutFlow extends LoggedInFlow { public class LogoutFlow extends LoggedInFlow {
@Inject LogoutFlow() {}
@Override @Override
public final EppOutput run() throws EppException { public final EppOutput run() throws EppException {
sessionMetadata.invalidate(); sessionMetadata.invalidate();

View file

@ -20,6 +20,7 @@ import google.registry.flows.CheckApiAction;
import google.registry.flows.CheckApiAction.CheckApiModule; import google.registry.flows.CheckApiAction.CheckApiModule;
import google.registry.flows.EppConsoleAction; import google.registry.flows.EppConsoleAction;
import google.registry.flows.EppTlsAction; import google.registry.flows.EppTlsAction;
import google.registry.flows.FlowComponent;
import google.registry.flows.TlsCredentials.EppTlsModule; import google.registry.flows.TlsCredentials.EppTlsModule;
import google.registry.rdap.RdapAutnumAction; import google.registry.rdap.RdapAutnumAction;
import google.registry.rdap.RdapDomainAction; import google.registry.rdap.RdapDomainAction;
@ -57,6 +58,7 @@ interface FrontendRequestComponent {
ConsoleUiAction consoleUiAction(); ConsoleUiAction consoleUiAction();
EppConsoleAction eppConsoleAction(); EppConsoleAction eppConsoleAction();
EppTlsAction eppTlsAction(); EppTlsAction eppTlsAction();
FlowComponent.Builder flowComponentBuilder();
RdapAutnumAction rdapAutnumAction(); RdapAutnumAction rdapAutnumAction();
RegistrarPaymentAction registrarPaymentAction(); RegistrarPaymentAction registrarPaymentAction();
RegistrarPaymentSetupAction registrarPaymentSetupAction(); RegistrarPaymentSetupAction registrarPaymentSetupAction();

View file

@ -19,6 +19,7 @@ import dagger.Subcomponent;
import google.registry.export.PublishDetailReportAction; import google.registry.export.PublishDetailReportAction;
import google.registry.flows.EppToolAction; import google.registry.flows.EppToolAction;
import google.registry.flows.EppToolAction.EppToolModule; import google.registry.flows.EppToolAction.EppToolModule;
import google.registry.flows.FlowComponent;
import google.registry.loadtest.LoadTestAction; import google.registry.loadtest.LoadTestAction;
import google.registry.loadtest.LoadTestModule; import google.registry.loadtest.LoadTestModule;
import google.registry.mapreduce.MapreduceModule; import google.registry.mapreduce.MapreduceModule;
@ -62,6 +63,7 @@ interface ToolsRequestComponent {
DeleteEntityAction deleteEntityAction(); DeleteEntityAction deleteEntityAction();
DeleteProberDataAction deleteProberDataAction(); DeleteProberDataAction deleteProberDataAction();
EppToolAction eppToolAction(); EppToolAction eppToolAction();
FlowComponent.Builder flowComponentBuilder();
GenerateZoneFilesAction generateZoneFilesAction(); GenerateZoneFilesAction generateZoneFilesAction();
KillAllCommitLogsAction killAllCommitLogsAction(); KillAllCommitLogsAction killAllCommitLogsAction();
KillAllEppResourcesAction killAllEppResourcesAction(); KillAllEppResourcesAction killAllEppResourcesAction();

View file

@ -31,6 +31,7 @@ java_library(
"//java/com/google/common/net", "//java/com/google/common/net",
"//third_party/java/appengine:appengine-api-testonly", "//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/appengine:appengine-testing", "//third_party/java/appengine:appengine-testing",
"//third_party/java/dagger",
"//third_party/java/joda_money", "//third_party/java/joda_money",
"//third_party/java/joda_time", "//third_party/java/joda_time",
"//third_party/java/json_simple", "//third_party/java/json_simple",

View file

@ -19,17 +19,16 @@ import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain; import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistReservedList; import static google.registry.testing.DatastoreHelper.persistReservedList;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static org.mockito.Mockito.mock;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.config.RegistryEnvironment; import google.registry.config.RegistryEnvironment;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
import google.registry.util.SystemClock;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import org.junit.Before; import org.junit.Before;
@ -66,9 +65,11 @@ public class CheckApiActionTest {
action.domain = domain; action.domain = domain;
action.response = new FakeResponse(); action.response = new FakeResponse();
action.config = RegistryEnvironment.UNITTEST.config(); action.config = RegistryEnvironment.UNITTEST.config();
action.eppController = new EppController(); action.eppController = DaggerEppTestComponent.builder()
action.eppController.clock = new SystemClock(); .fakesAndMocksModule(new FakesAndMocksModule(new FakeClock()))
action.eppController.metrics = mock(EppMetrics.class); .build()
.startRequest()
.eppController();
action.run(); action.run();
return (Map<String, Object>) JSONValue.parse(((FakeResponse) action.response).getPayload()); return (Map<String, Object>) JSONValue.parse(((FakeResponse) action.response).getPayload());
} }

View file

@ -0,0 +1,171 @@
// Copyright 2016 The Domain Registry 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.flows;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadAtPointInTime;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistActiveHost;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.time.DateTimeZone.UTC;
import static org.joda.time.Duration.standardDays;
import com.googlecode.objectify.Key;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.domain.DomainResource;
import google.registry.model.ofy.Ofy;
import google.registry.testing.AppEngineRule;
import google.registry.testing.EppLoader;
import google.registry.testing.ExceptionRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeHttpSession;
import google.registry.testing.InjectRule;
import google.registry.testing.ShardableTestCase;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Test that domain flows create the commit logs needed to reload at points in the past. */
@RunWith(JUnit4.class)
public class EppCommitLogsTest extends ShardableTestCase {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withTaskQueue()
.build();
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final InjectRule inject = new InjectRule();
private final FakeClock clock = new FakeClock(DateTime.now(UTC));
private EppLoader eppLoader;
@Before
public void init() throws Exception {
createTld("tld");
inject.setStaticField(Ofy.class, "clock", clock);
}
private void runFlow() throws Exception {
SessionMetadata sessionMetadata = new HttpSessionMetadata(new FakeHttpSession());
sessionMetadata.setClientId("TheRegistrar");
DaggerEppTestComponent.builder()
.fakesAndMocksModule(new FakesAndMocksModule(clock))
.build()
.startRequest()
.flowComponentBuilder()
.flowModule(new FlowModule.Builder()
.setSessionMetadata(sessionMetadata)
.setCredentials(new PasswordOnlyTransportCredentials())
.setEppRequestSource(EppRequestSource.UNIT_TEST)
.setIsDryRun(false)
.setIsSuperuser(false)
.setInputXmlBytes(eppLoader.getEppXml().getBytes(UTF_8))
.setEppInput(eppLoader.getEpp())
.build())
.build()
.flowRunner()
.run();
}
@Test
public void testLoadAtPointInTime() throws Exception {
clock.setTo(DateTime.parse("1984-12-18T12:30Z")); // not midnight
persistActiveHost("ns1.example.net");
persistActiveHost("ns2.example.net");
persistActiveContact("jd1234");
persistActiveContact("sh8013");
clock.advanceBy(standardDays(1));
DateTime timeAtCreate = clock.nowUtc();
clock.setTo(timeAtCreate);
eppLoader = new EppLoader(this, "domain_create.xml");
runFlow();
ofy().clearSessionCache();
Key<DomainResource> key = Key.create(ofy().load().type(DomainResource.class).first().now());
DomainResource domainAfterCreate = ofy().load().key(key).now();
assertThat(domainAfterCreate.getFullyQualifiedDomainName()).isEqualTo("example.tld");
clock.advanceBy(standardDays(2));
DateTime timeAtFirstUpdate = clock.nowUtc();
eppLoader = new EppLoader(this, "domain_update_dsdata_add.xml");
runFlow();
ofy().clearSessionCache();
DomainResource domainAfterFirstUpdate = ofy().load().key(key).now();
assertThat(domainAfterCreate).isNotEqualTo(domainAfterFirstUpdate);
clock.advanceOneMilli(); // same day as first update
DateTime timeAtSecondUpdate = clock.nowUtc();
eppLoader = new EppLoader(this, "domain_update_dsdata_rem.xml");
runFlow();
ofy().clearSessionCache();
DomainResource domainAfterSecondUpdate = ofy().load().key(key).now();
clock.advanceBy(standardDays(2));
DateTime timeAtDelete = clock.nowUtc(); // before 'add' grace period ends
eppLoader = new EppLoader(this, "domain_delete.xml");
runFlow();
ofy().clearSessionCache();
assertThat(domainAfterFirstUpdate).isNotEqualTo(domainAfterSecondUpdate);
// Point-in-time can only rewind an object from the current version, not roll forward.
DomainResource latest = ofy().load().key(key).now();
// Creation time has millisecond granularity due to isActive() check.
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtCreate.minusMillis(1)).now()).isNull();
assertThat(loadAtPointInTime(latest, timeAtCreate).now()).isNotNull();
assertThat(loadAtPointInTime(latest, timeAtCreate.plusMillis(1)).now()).isNotNull();
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtCreate.plusDays(1)).now())
.isEqualTo(domainAfterCreate);
// Both updates happened on the same day. Since the revisions field has day granularity, the
// reference to the first update should have been overwritten by the second, and its timestamp
// rolled forward. So we have to fall back to the last revision before midnight.
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtFirstUpdate).now())
.isEqualTo(domainAfterCreate);
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtSecondUpdate).now())
.isEqualTo(domainAfterSecondUpdate);
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtSecondUpdate.plusDays(1)).now())
.isEqualTo(domainAfterSecondUpdate);
// Deletion time has millisecond granularity due to isActive() check.
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtDelete.minusMillis(1)).now()).isNotNull();
assertThat(loadAtPointInTime(latest, timeAtDelete).now()).isNull();
assertThat(loadAtPointInTime(latest, timeAtDelete.plusMillis(1)).now()).isNull();
}
}

View file

@ -15,6 +15,7 @@
package google.registry.flows; package google.registry.flows;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -44,7 +45,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
@Before @Before
public void initTld() { public void initTld() {
createTld("example"); createTlds("example", "tld");
} }
/** Create the two administrative contacts and two hosts. */ /** Create the two administrative contacts and two hosts. */
@ -129,7 +130,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
"domain_create_response.xml", "domain_create_response.xml",
DateTime.parse("2000-06-01T00:02:00Z")); DateTime.parse("2000-06-01T00:02:00Z"));
// Delete domain example.com after its add grace period has expired. // Delete domain example.tld after its add grace period has expired.
assertCommandAndResponse( assertCommandAndResponse(
"domain_delete.xml", "domain_delete.xml",
"generic_success_action_pending_response.xml", "generic_success_action_pending_response.xml",
@ -338,7 +339,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-01T00:01:00Z")); DateTime.parse("2001-01-01T00:01:00Z"));
assertCommandAndResponse( assertCommandAndResponse(
"poll_ack.xml", "poll_ack.xml",
ImmutableMap.of("ID", "1-A-EXAMPLE-16-20"), ImmutableMap.of("ID", "1-B-EXAMPLE-17-21"),
"poll_ack_response_empty.xml", "poll_ack_response_empty.xml",
null, null,
DateTime.parse("2001-01-01T00:01:00Z")); DateTime.parse("2001-01-01T00:01:00Z"));
@ -350,7 +351,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-06T00:01:00Z")); DateTime.parse("2001-01-06T00:01:00Z"));
assertCommandAndResponse( assertCommandAndResponse(
"poll_ack.xml", "poll_ack.xml",
ImmutableMap.of("ID", "1-A-EXAMPLE-16-22"), ImmutableMap.of("ID", "1-B-EXAMPLE-17-23"),
"poll_ack_response_empty.xml", "poll_ack_response_empty.xml",
null, null,
DateTime.parse("2001-01-06T00:01:00Z")); DateTime.parse("2001-01-06T00:01:00Z"));
@ -366,7 +367,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-06T00:02:00Z")); DateTime.parse("2001-01-06T00:02:00Z"));
assertCommandAndResponse( assertCommandAndResponse(
"poll_ack.xml", "poll_ack.xml",
ImmutableMap.of("ID", "1-A-EXAMPLE-16-21"), ImmutableMap.of("ID", "1-B-EXAMPLE-17-22"),
"poll_ack_response_empty.xml", "poll_ack_response_empty.xml",
null, null,
DateTime.parse("2001-01-06T00:02:00Z")); DateTime.parse("2001-01-06T00:02:00Z"));

View file

@ -21,12 +21,11 @@ import static google.registry.xml.XmlTestUtils.assertXmlEqualsWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_OK; import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import static org.mockito.Mockito.mock;
import com.google.common.net.MediaType; import com.google.common.net.MediaType;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.ofy.Ofy; import google.registry.model.ofy.Ofy;
import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeHttpSession; import google.registry.testing.FakeHttpSession;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
@ -100,8 +99,7 @@ public class EppTestCase extends ShardableTestCase {
// When a session is invalidated, reset the sessionMetadata field. // When a session is invalidated, reset the sessionMetadata field.
super.invalidate(); super.invalidate();
EppTestCase.this.sessionMetadata = null; EppTestCase.this.sessionMetadata = null;
} }};
};
} }
String actualOutput = executeXmlCommand(input); String actualOutput = executeXmlCommand(input);
assertXmlEqualsWithMessage( assertXmlEqualsWithMessage(
@ -118,14 +116,16 @@ public class EppTestCase extends ShardableTestCase {
EppRequestHandler handler = new EppRequestHandler(); EppRequestHandler handler = new EppRequestHandler();
FakeResponse response = new FakeResponse(); FakeResponse response = new FakeResponse();
handler.response = response; handler.response = response;
handler.eppController = new EppController(); handler.eppController = DaggerEppTestComponent.builder()
handler.eppController.clock = clock; .fakesAndMocksModule(new FakesAndMocksModule(clock))
handler.eppController.metrics = mock(EppMetrics.class); .build()
.startRequest()
.eppController();
handler.executeEpp( handler.executeEpp(
sessionMetadata, sessionMetadata,
credentials, credentials,
EppRequestSource.UNIT_TEST, EppRequestSource.UNIT_TEST,
false, false, // Not dryRun.
isSuperuser, isSuperuser,
inputXml.getBytes(UTF_8)); inputXml.getBytes(UTF_8));
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);

View file

@ -0,0 +1,70 @@
// Copyright 2016 The Domain Registry 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.flows;
import static org.mockito.Mockito.mock;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.request.RequestScope;
import google.registry.testing.FakeClock;
import google.registry.util.Clock;
import javax.inject.Singleton;
/** Dagger component for running EPP tests. */
@Singleton
@Component(
modules = {
EppTestComponent.FakesAndMocksModule.class
})
interface EppTestComponent {
RequestComponent startRequest();
/** Module for injecting fakes and mocks. */
@Module
static class FakesAndMocksModule {
final FakeClock clock;
final EppMetrics metrics;
FakesAndMocksModule(FakeClock clock) {
this.clock = clock;
this.metrics = mock(EppMetrics.class);
}
@Provides
Clock provideClock() {
return clock;
}
@Provides
EppMetrics provideMetrics() {
return metrics;
}
}
/** Subcomponent for request scoped injections. */
@RequestScope
@Subcomponent
interface RequestComponent {
EppController eppController();
FlowComponent.Builder flowComponentBuilder();
}
}

View file

@ -34,13 +34,12 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.flows.picker.FlowPicker; import google.registry.flows.picker.FlowPicker;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.GracePeriod; import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.ProtocolDefinition; import google.registry.model.eppcommon.ProtocolDefinition;
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.model.ofy.Ofy; import google.registry.model.ofy.Ofy;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
@ -86,8 +85,6 @@ public abstract class FlowTestCase<F extends Flow> {
@Rule @Rule
public final InjectRule inject = new InjectRule(); public final InjectRule inject = new InjectRule();
private Class<? extends Flow> flowClass;
protected EppLoader eppLoader; protected EppLoader eppLoader;
protected SessionMetadata sessionMetadata; protected SessionMetadata sessionMetadata;
protected FakeClock clock = new FakeClock(DateTime.now(UTC)); protected FakeClock clock = new FakeClock(DateTime.now(UTC));
@ -120,29 +117,8 @@ public abstract class FlowTestCase<F extends Flow> {
return readResourceUtf8(getClass(), "testdata/" + filename); return readResourceUtf8(getClass(), "testdata/" + filename);
} }
/** Load a flow from an epp object. */ protected String getClientTrid() throws Exception {
private FlowRunner getFlowRunner(CommitMode commitMode, UserPrivileges userPrivileges) return eppLoader.getEpp().getCommandWrapper().getClTrid();
throws Exception {
EppInput eppInput = eppLoader.getEpp();
flowClass = firstNonNull(flowClass, FlowPicker.getFlowClass(eppInput));
Class<?> expectedFlowClass = new TypeInstantiator<F>(getClass()){}.getExactType();
assertThat(flowClass).isEqualTo(expectedFlowClass);
return new FlowRunner(
flowClass,
eppInput,
getTrid(),
sessionMetadata,
credentials,
eppRequestSource,
commitMode.equals(CommitMode.DRY_RUN),
userPrivileges.equals(UserPrivileges.SUPERUSER),
"<xml></xml>".getBytes(),
null,
clock);
}
protected Trid getTrid() throws Exception {
return Trid.create(eppLoader.getEpp().getCommandWrapper().getClTrid(), "server-trid");
} }
/** Gets the client ID that the flow will run as. */ /** Gets the client ID that the flow will run as. */
@ -156,8 +132,15 @@ public abstract class FlowTestCase<F extends Flow> {
} }
public void assertTransactionalFlow(boolean isTransactional) throws Exception { public void assertTransactionalFlow(boolean isTransactional) throws Exception {
assertThat(getFlowRunner(CommitMode.LIVE, UserPrivileges.NORMAL).isTransactional()) Class<? extends Flow> flowClass = FlowPicker.getFlowClass(eppLoader.getEpp());
.isEqualTo(isTransactional); if (isTransactional) {
assertThat(flowClass).isAssignableTo(TransactionalFlow.class);
} else {
// There's no "isNotAssignableTo" in Truth.
assertThat(TransactionalFlow.class.isAssignableFrom(flowClass))
.named(flowClass.getSimpleName() + " implements TransactionalFlow")
.isFalse();
}
} }
public void assertNoHistory() throws Exception { public void assertNoHistory() throws Exception {
@ -273,36 +256,67 @@ public abstract class FlowTestCase<F extends Flow> {
.containsExactlyElementsIn(FluentIterable.from(asList(expected)).transform(idStripper)); .containsExactlyElementsIn(FluentIterable.from(asList(expected)).transform(idStripper));
} }
/** Run a flow, and attempt to marshal the result to EPP or throw if it doesn't validate. */ private EppOutput runFlowInternal(CommitMode commitMode, UserPrivileges userPrivileges)
throws Exception {
// Assert that the xml triggers the flow we expect.
assertThat(FlowPicker.getFlowClass(eppLoader.getEpp()))
.isEqualTo(new TypeInstantiator<F>(getClass()){}.getExactType());
// Run the flow.
return DaggerEppTestComponent.builder()
.fakesAndMocksModule(new FakesAndMocksModule(clock))
.build()
.startRequest()
.flowComponentBuilder()
.flowModule(new FlowModule.Builder()
.setSessionMetadata(sessionMetadata)
.setCredentials(credentials)
.setEppRequestSource(eppRequestSource)
.setIsDryRun(commitMode.equals(CommitMode.DRY_RUN))
.setIsSuperuser(userPrivileges.equals(UserPrivileges.SUPERUSER))
.setInputXmlBytes(eppLoader.getEppXml().getBytes(UTF_8))
.setEppInput(eppLoader.getEpp())
.build())
.build()
.flowRunner()
.run();
}
/** Run a flow and marshal the result to EPP, or throw if it doesn't validate. */
public EppOutput runFlow(CommitMode commitMode, UserPrivileges userPrivileges) throws Exception { public EppOutput runFlow(CommitMode commitMode, UserPrivileges userPrivileges) throws Exception {
EppOutput output = getFlowRunner(commitMode, userPrivileges).run(); EppOutput output = runFlowInternal(commitMode, userPrivileges);
marshal(output, ValidationMode.STRICT); marshal(output, ValidationMode.STRICT);
return output; return output;
} }
/** Shortcut to call {@link #runFlow(CommitMode, UserPrivileges)} as normal user and live run. */
public EppOutput runFlow() throws Exception { public EppOutput runFlow() throws Exception {
return runFlow(CommitMode.LIVE, UserPrivileges.NORMAL); return runFlow(CommitMode.LIVE, UserPrivileges.NORMAL);
} }
/** Run a flow, marshal the result to EPP, and assert that the output is as expected. */
public void runFlowAssertResponse( public void runFlowAssertResponse(
CommitMode commitMode, UserPrivileges userPrivileges, String xml, String... ignoredPaths) CommitMode commitMode, UserPrivileges userPrivileges, String xml, String... ignoredPaths)
throws Exception { throws Exception {
EppOutput eppOutput = getFlowRunner(commitMode, userPrivileges).run(); // Always ignore the server trid, since it's generated and meaningless to flow correctness.
if (eppOutput.isResponse()) { String[] ignoredPathsPlusTrid = FluentIterable.from(ignoredPaths)
assertThat(eppOutput.isSuccess()).isTrue(); .append("epp.response.trID.svTRID")
.toArray(String.class);
EppOutput output = runFlowInternal(commitMode, userPrivileges);
if (output.isResponse()) {
assertThat(output.isSuccess()).isTrue();
} }
try { try {
assertXmlEquals( assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.STRICT), UTF_8), ignoredPaths); xml, new String(marshal(output, ValidationMode.STRICT), UTF_8), ignoredPathsPlusTrid);
} catch (Throwable e) { } catch (Throwable e) {
assertXmlEquals( assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.LENIENT), UTF_8), ignoredPaths); xml, new String(marshal(output, ValidationMode.LENIENT), UTF_8), ignoredPathsPlusTrid);
// If it was a marshaling error, augment the output. // If it was a marshaling error, augment the output.
throw new Exception( throw new Exception(
String.format( String.format(
"Invalid xml.\nExpected:\n%s\n\nActual:\n%s\n", "Invalid xml.\nExpected:\n%s\n\nActual:\n%s\n",
xml, xml,
marshal(eppOutput, ValidationMode.LENIENT)), marshal(output, ValidationMode.LENIENT)),
e); e);
} }
// Clear the cache so that we don't see stale results in tests. // Clear the cache so that we don't see stale results in tests.

View file

@ -71,7 +71,7 @@ public class ContactTransferRequestFlowTest
.hasTransferStatus(TransferStatus.PENDING).and() .hasTransferStatus(TransferStatus.PENDING).and()
.hasTransferGainingClientId("NewRegistrar").and() .hasTransferGainingClientId("NewRegistrar").and()
.hasTransferLosingClientId("TheRegistrar").and() .hasTransferLosingClientId("TheRegistrar").and()
.hasTransferRequestTrid(getTrid()).and() .hasTransferRequestClientTrid(getClientTrid()).and()
.hasCurrentSponsorClientId("TheRegistrar").and() .hasCurrentSponsorClientId("TheRegistrar").and()
.hasPendingTransferExpirationTime(afterTransfer).and() .hasPendingTransferExpirationTime(afterTransfer).and()
.hasOnlyOneHistoryEntryWhich() .hasOnlyOneHistoryEntryWhich()

View file

@ -64,7 +64,6 @@ import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.poll.PendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
@ -95,7 +94,7 @@ public class DomainTransferRequestFlowTest
.hasTransferStatus(TransferStatus.PENDING).and() .hasTransferStatus(TransferStatus.PENDING).and()
.hasTransferGainingClientId("NewRegistrar").and() .hasTransferGainingClientId("NewRegistrar").and()
.hasTransferLosingClientId("TheRegistrar").and() .hasTransferLosingClientId("TheRegistrar").and()
.hasTransferRequestTrid(getTrid()).and() .hasTransferRequestClientTrid(getClientTrid()).and()
.hasCurrentSponsorClientId("TheRegistrar").and() .hasCurrentSponsorClientId("TheRegistrar").and()
.hasPendingTransferExpirationTime( .hasPendingTransferExpirationTime(
clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength())).and() clock.nowUtc().plus(Registry.get("tld").getAutomaticTransferLength())).and()
@ -228,8 +227,7 @@ public class DomainTransferRequestFlowTest
PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable PendingActionNotificationResponse panData = Iterables.getOnlyElement(FluentIterable
.from(transferApprovedPollMessage.getResponseData()) .from(transferApprovedPollMessage.getResponseData())
.filter(PendingActionNotificationResponse.class)); .filter(PendingActionNotificationResponse.class));
assertThat(panData.getTrid()) assertThat(panData.getTrid().getClientTransactionId()).isEqualTo("ABC-12345");
.isEqualTo(Trid.create("ABC-12345", "server-trid"));
assertThat(panData.getActionResult()).isTrue(); assertThat(panData.getActionResult()).isTrue();
// Two poll messages on the losing registrar's side at the implicit transfer time: a // Two poll messages on the losing registrar's side at the implicit transfer time: a

View file

@ -3,7 +3,7 @@
<create> <create>
<domain:create <domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.example</domain:name> <domain:name>example.tld</domain:name>
<domain:period unit="y">2</domain:period> <domain:period unit="y">2</domain:period>
<domain:registrant>jd1234</domain:registrant> <domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact> <domain:contact type="admin">sh8013</domain:contact>

View file

@ -6,7 +6,7 @@
<resData> <resData>
<domain:creData <domain:creData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.example</domain:name> <domain:name>example.tld</domain:name>
<domain:crDate>2000-06-01T00:02:00.0Z</domain:crDate> <domain:crDate>2000-06-01T00:02:00.0Z</domain:crDate>
<domain:exDate>2002-06-01T00:02:00.0Z</domain:exDate> <domain:exDate>2002-06-01T00:02:00.0Z</domain:exDate>
</domain:creData> </domain:creData>

View file

@ -3,7 +3,7 @@
<delete> <delete>
<domain:delete <domain:delete
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.example</domain:name> <domain:name>example.tld</domain:name>
</domain:delete> </domain:delete>
</delete> </delete>
<clTRID>ABC-12345</clTRID> <clTRID>ABC-12345</clTRID>

View file

@ -3,7 +3,7 @@
<info> <info>
<domain:info <domain:info
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name hosts="all">example.example</domain:name> <domain:name hosts="all">example.tld</domain:name>
</domain:info> </domain:info>
</info> </info>
<clTRID>ABC-12345</clTRID> <clTRID>ABC-12345</clTRID>

View file

@ -7,7 +7,7 @@
<resData> <resData>
<domain:infData <domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.example</domain:name> <domain:name>example.tld</domain:name>
<domain:roid>%ROID%</domain:roid> <domain:roid>%ROID%</domain:roid>
<domain:status s="inactive"/> <domain:status s="inactive"/>
<domain:status s="pendingDelete"/> <domain:status s="pendingDelete"/>

View file

@ -3,7 +3,7 @@
<update> <update>
<domain:update <domain:update
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.example</domain:name> <domain:name>example.tld</domain:name>
<domain:chg/> <domain:chg/>
</domain:update> </domain:update>
</update> </update>

View file

@ -3,7 +3,7 @@
<result code="1301"> <result code="1301">
<msg>Command completed successfully; ack to dequeue</msg> <msg>Command completed successfully; ack to dequeue</msg>
</result> </result>
<msgQ count="1" id="1-A-EXAMPLE-16-20"> <msgQ count="1" id="1-B-EXAMPLE-17-21">
<qDate>2001-01-01T00:00:00Z</qDate> <qDate>2001-01-01T00:00:00Z</qDate>
<msg>Transfer requested.</msg> <msg>Transfer requested.</msg>
</msgQ> </msgQ>

View file

@ -3,7 +3,7 @@
<result code="1301"> <result code="1301">
<msg>Command completed successfully; ack to dequeue</msg> <msg>Command completed successfully; ack to dequeue</msg>
</result> </result>
<msgQ count="1" id="1-A-EXAMPLE-16-22"> <msgQ count="1" id="1-B-EXAMPLE-17-23">
<qDate>2001-01-06T00:00:00Z</qDate> <qDate>2001-01-06T00:00:00Z</qDate>
<msg>Transfer approved.</msg> <msg>Transfer approved.</msg>
</msgQ> </msgQ>

View file

@ -4,7 +4,7 @@
<result code="1301"> <result code="1301">
<msg>Command completed successfully; ack to dequeue</msg> <msg>Command completed successfully; ack to dequeue</msg>
</result> </result>
<msgQ count="1" id="1-A-EXAMPLE-16-21"> <msgQ count="1" id="1-B-EXAMPLE-17-22">
<qDate>2001-01-06T00:00:00Z</qDate> <qDate>2001-01-06T00:00:00Z</qDate>
<msg>Transfer approved.</msg> <msg>Transfer approved.</msg>
</msgQ> </msgQ>

View file

@ -15,35 +15,19 @@
package google.registry.model; package google.registry.model;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.flows.picker.FlowPicker.getFlowClass;
import static google.registry.model.EppResourceUtils.loadAtPointInTime; import static google.registry.model.EppResourceUtils.loadAtPointInTime;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newHostResource; import static google.registry.testing.DatastoreHelper.newHostResource;
import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistActiveHost;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistResourceWithCommitLog; import static google.registry.testing.DatastoreHelper.persistResourceWithCommitLog;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import static org.joda.time.Duration.standardDays;
import com.googlecode.objectify.Key;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowRunner;
import google.registry.flows.HttpSessionMetadata;
import google.registry.flows.PasswordOnlyTransportCredentials;
import google.registry.flows.SessionMetadata;
import google.registry.model.domain.DomainResource;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.ofy.Ofy; import google.registry.model.ofy.Ofy;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.testing.EppLoader;
import google.registry.testing.ExceptionRule; import google.registry.testing.ExceptionRule;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeHttpSession;
import google.registry.testing.InjectRule; import google.registry.testing.InjectRule;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -71,7 +55,6 @@ public class EppResourceUtilsTest {
public final InjectRule inject = new InjectRule(); public final InjectRule inject = new InjectRule();
private final FakeClock clock = new FakeClock(DateTime.now(UTC)); private final FakeClock clock = new FakeClock(DateTime.now(UTC));
private EppLoader eppLoader;
@Before @Before
public void init() throws Exception { public void init() throws Exception {
@ -79,103 +62,6 @@ public class EppResourceUtilsTest {
inject.setStaticField(Ofy.class, "clock", clock); inject.setStaticField(Ofy.class, "clock", clock);
} }
private void runFlow() throws Exception {
SessionMetadata sessionMetadata = new HttpSessionMetadata(new FakeHttpSession());
sessionMetadata.setClientId("TheRegistrar");
new FlowRunner(
getFlowClass(eppLoader.getEpp()),
eppLoader.getEpp(),
Trid.create(null, "server-trid"),
sessionMetadata,
new PasswordOnlyTransportCredentials(),
EppRequestSource.UNIT_TEST,
false,
false,
"<xml></xml>".getBytes(),
null,
clock)
.run();
}
/** Test that update flow creates commit logs needed to reload at any arbitrary time. */
@Test
public void testLoadAtPointInTime() throws Exception {
clock.setTo(DateTime.parse("1984-12-18T12:30Z")); // not midnight
persistActiveHost("ns1.example.net");
persistActiveHost("ns2.example.net");
persistActiveContact("jd1234");
persistActiveContact("sh8013");
clock.advanceBy(standardDays(1));
DateTime timeAtCreate = clock.nowUtc();
clock.setTo(timeAtCreate);
eppLoader = new EppLoader(this, "domain_create.xml");
runFlow();
ofy().clearSessionCache();
Key<DomainResource> key = Key.create(ofy().load().type(DomainResource.class).first().now());
DomainResource domainAfterCreate = ofy().load().key(key).now();
assertThat(domainAfterCreate.getFullyQualifiedDomainName()).isEqualTo("example.tld");
clock.advanceBy(standardDays(2));
DateTime timeAtFirstUpdate = clock.nowUtc();
eppLoader = new EppLoader(this, "domain_update_dsdata_add.xml");
runFlow();
ofy().clearSessionCache();
DomainResource domainAfterFirstUpdate = ofy().load().key(key).now();
assertThat(domainAfterCreate).isNotEqualTo(domainAfterFirstUpdate);
clock.advanceOneMilli(); // same day as first update
DateTime timeAtSecondUpdate = clock.nowUtc();
eppLoader = new EppLoader(this, "domain_update_dsdata_rem.xml");
runFlow();
ofy().clearSessionCache();
DomainResource domainAfterSecondUpdate = ofy().load().key(key).now();
clock.advanceBy(standardDays(2));
DateTime timeAtDelete = clock.nowUtc(); // before 'add' grace period ends
eppLoader = new EppLoader(this, "domain_delete.xml");
runFlow();
ofy().clearSessionCache();
assertThat(domainAfterFirstUpdate).isNotEqualTo(domainAfterSecondUpdate);
// Point-in-time can only rewind an object from the current version, not roll forward.
DomainResource latest = ofy().load().key(key).now();
// Creation time has millisecond granularity due to isActive() check.
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtCreate.minusMillis(1)).now()).isNull();
assertThat(loadAtPointInTime(latest, timeAtCreate).now()).isNotNull();
assertThat(loadAtPointInTime(latest, timeAtCreate.plusMillis(1)).now()).isNotNull();
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtCreate.plusDays(1)).now())
.isEqualTo(domainAfterCreate);
// Both updates happened on the same day. Since the revisions field has day granularity, the
// reference to the first update should have been overwritten by the second, and its timestamp
// rolled forward. So we have to fall back to the last revision before midnight.
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtFirstUpdate).now())
.isEqualTo(domainAfterCreate);
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtSecondUpdate).now())
.isEqualTo(domainAfterSecondUpdate);
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtSecondUpdate.plusDays(1)).now())
.isEqualTo(domainAfterSecondUpdate);
// Deletion time has millisecond granularity due to isActive() check.
ofy().clearSessionCache();
assertThat(loadAtPointInTime(latest, timeAtDelete.minusMillis(1)).now()).isNotNull();
assertThat(loadAtPointInTime(latest, timeAtDelete).now()).isNull();
assertThat(loadAtPointInTime(latest, timeAtDelete.plusMillis(1)).now()).isNull();
}
@Test @Test
public void testLoadAtPointInTime_beforeCreated_returnsNull() throws Exception { public void testLoadAtPointInTime_beforeCreated_returnsNull() throws Exception {
clock.advanceOneMilli(); clock.advanceOneMilli();

View file

@ -1,11 +0,0 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<delete>
<domain:delete
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
</domain:delete>
</delete>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -26,7 +26,6 @@ import com.google.common.truth.Subject;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.testing.TruthChainer.And; import google.registry.testing.TruthChainer.And;
@ -205,10 +204,10 @@ abstract class AbstractEppResourceSubject
"has transferStatus"); "has transferStatus");
} }
public And<S> hasTransferRequestTrid(Trid trid) { public And<S> hasTransferRequestClientTrid(String clTrid) {
return hasValue( return hasValue(
trid, clTrid,
getSubject().getTransferData().getTransferRequestTrid(), getSubject().getTransferData().getTransferRequestTrid().getClientTransactionId(),
"has trid"); "has trid");
} }