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;
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.base.Joiner;
import com.google.common.collect.ImmutableList;
import google.registry.flows.FlowModule.EppExceptionInProviderException;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput;
@ -43,6 +42,7 @@ public final class EppController {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
@Inject Clock clock;
@Inject FlowComponent.Builder flowComponentBuilder;
@Inject EppMetrics metrics;
@Inject EppController() {}
@ -54,57 +54,62 @@ public final class EppController {
boolean isDryRun,
boolean isSuperuser,
byte[] inputXmlBytes) {
Trid trid = null;
metrics.setClientId(sessionMetadata.getClientId());
metrics.setPrivilegeLevel(isSuperuser ? "SUPERUSER" : "NORMAL");
try {
EppInput eppInput = unmarshal(EppInput.class, inputXmlBytes);
trid = Trid.create(eppInput.getCommandWrapper().getClTrid());
ImmutableList<String> targetIds = eppInput.getTargetIds();
EppInput eppInput;
try {
eppInput = unmarshal(EppInput.class, inputXmlBytes);
} catch (EppException e) {
// Send the client an error message, with no clTRID since we couldn't unmarshal it.
metrics.setEppStatus(e.getResult().getCode());
return getErrorResponse(clock, e.getResult(), Trid.create(null));
}
metrics.setCommandName(eppInput.getCommandName());
metrics.setClientId(sessionMetadata.getClientId());
metrics.setPrivilegeLevel(isSuperuser ? "SUPERUSER" : "NORMAL");
if (!targetIds.isEmpty()) {
metrics.setEppTarget(Joiner.on(",").join(targetIds));
if (!eppInput.getTargetIds().isEmpty()) {
metrics.setEppTarget(Joiner.on(',').join(eppInput.getTargetIds()));
}
FlowRunner flowRunner = new FlowRunner(
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());
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 eppOutput;
} catch (EppException e) {
// The command failed. Send the client an error message.
metrics.setEppStatus(e.getResult().getCode());
return getErrorResponse(clock, e.getResult(), trid);
} catch (Throwable e) {
// Something bad and unexpected happened. Send the client a generic error, and log it.
logger.severe(e, "Unexpected failure");
metrics.setEppStatus(Code.CommandFailed);
return getErrorResponse(clock, Result.create(Code.CommandFailed), trid);
return output;
} finally {
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
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()
.setTrid(trid == null ? Trid.create(null) : trid)
.setResult(result)
.setTrid(trid)
.setExecutionTime(clock.nowUtc())
.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 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.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput;
import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.TypeUtils;
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. */
public class FlowRunner {
@ -40,43 +48,20 @@ public class FlowRunner {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
private final Class<? extends Flow> flowClass;
private final EppInput eppInput;
private final Trid trid;
private final SessionMetadata sessionMetadata;
private final TransportCredentials credentials;
private final EppRequestSource eppRequestSource;
private final boolean isDryRun;
private final boolean isSuperuser;
private final byte[] inputXmlBytes;
private final EppMetrics metrics;
private final Clock clock;
public 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;
}
@Inject @Nullable @ClientId String clientId;
@Inject Clock clock;
@Inject TransportCredentials credentials;
@Inject EppInput eppInput;
@Inject EppRequestSource eppRequestSource;
@Inject Provider<Flow> flowProvider;
@Inject @InputXml byte[] inputXmlBytes;
@Inject @DryRun boolean isDryRun;
@Inject @Superuser boolean isSuperuser;
@Inject @Transactional boolean isTransactional;
@Inject EppMetrics metrics;
@Inject SessionMetadata sessionMetadata;
@Inject Trid trid;
@Inject FlowRunner() {}
public EppOutput run() throws EppException {
String clientId = sessionMetadata.getClientId();
@ -90,10 +75,8 @@ public class FlowRunner {
eppRequestSource,
isDryRun ? "DRY_RUN" : "LIVE",
isSuperuser ? "SUPERUSER" : "NORMAL");
if (!isTransactional()) {
if (metrics != null) {
metrics.incrementAttempts();
}
if (!isTransactional) {
metrics.incrementAttempts();
return createAndInitFlow(clock.nowUtc()).run();
}
// 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>() {
@Override
public EppOutput run() {
if (metrics != null) {
metrics.incrementAttempts();
}
metrics.incrementAttempts();
try {
EppOutput output = createAndInitFlow(ofy().getTransactionTime()).run();
if (isDryRun) {
@ -137,7 +118,7 @@ public class FlowRunner {
}
private Flow createAndInitFlow(DateTime now) throws EppException {
return TypeUtils.<Flow>instantiate(flowClass).init(
return flowProvider.get().init(
eppInput,
trid,
sessionMetadata,
@ -148,10 +129,6 @@ public class FlowRunner {
inputXmlBytes);
}
public boolean isTransactional() {
return TransactionalFlow.class.isAssignableFrom(flowClass);
}
/**
* 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 javax.inject.Inject;
/**
* An EPP flow that checks whether a contact can be provisioned.
*
* @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException}
*/
public class ContactCheckFlow extends ResourceCheckFlow<ContactResource, Check> {
@Inject ContactCheckFlow() {}
@Override
protected CheckData getCheckData() {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* An EPP flow that creates a new contact resource.
*
@ -37,6 +39,9 @@ import google.registry.model.reporting.HistoryEntry;
* @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException}
*/
public class ContactCreateFlow extends ResourceCreateFlow<ContactResource, Builder, Create> {
@Inject ContactCreateFlow() {}
@Override
protected EppOutput getOutput() {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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. */
private static final int FAILFAST_CHECK_COUNT = 5;
@Inject ContactDeleteFlow() {}
@Override
protected boolean isLinkedForFailfast(final Ref<ContactResource> ref) {
// 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.ContactResource;
import javax.inject.Inject;
/**
* An EPP flow that reads a contact.
*
* @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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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
extends ResourceTransferApproveFlow<ContactResource, Builder, Transfer> {
@Inject ContactTransferApproveFlow() {}
@Override
protected final HistoryEntry.Type getHistoryEntryType() {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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
extends ResourceTransferCancelFlow<ContactResource, Builder, Transfer> {
@Inject ContactTransferCancelFlow() {}
@Override
protected final HistoryEntry.Type getHistoryEntryType() {
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.ContactResource;
import javax.inject.Inject;
/**
* 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}
*/
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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
extends ResourceTransferRejectFlow<ContactResource, Builder, Transfer> {
@Inject ContactTransferRejectFlow() {}
@Override
protected final HistoryEntry.Type getHistoryEntryType() {
return HistoryEntry.Type.CONTACT_TRANSFER_REJECT;

View file

@ -22,6 +22,8 @@ import google.registry.model.reporting.HistoryEntry;
import org.joda.time.Duration;
import javax.inject.Inject;
/**
* An EPP flow that requests a transfer on a {@link ContactResource}.
*
@ -34,6 +36,8 @@ import org.joda.time.Duration;
public class ContactTransferRequestFlow
extends ResourceTransferRequestFlow<ContactResource, Transfer> {
@Inject ContactTransferRequestFlow() {}
@Override
protected final HistoryEntry.Type getHistoryEntryType() {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* An EPP flow that updates a contact resource.
*
@ -36,6 +38,9 @@ import google.registry.model.reporting.HistoryEntry;
* @error {@link ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException}
*/
public class ContactUpdateFlow extends ResourceUpdateFlow<ContactResource, Builder, Update> {
@Inject ContactUpdateFlow() {}
@Override
protected void verifyNewUpdatedStateIsAllowed() throws EppException {
validateAsciiPostalInfo(newResource.getInternationalizedPostalInfo());

View file

@ -35,6 +35,8 @@ import google.registry.model.tmch.ClaimsListShard;
import java.util.Map.Entry;
import javax.inject.Inject;
/**
* 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(
TldState.PREDELEGATION, TldState.SUNRISE);
@Inject ClaimsCheckFlow() {}
@Override
protected void initDomainCheckFlow() throws EppException {
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.tmch.LordnTask;
import javax.inject.Inject;
/**
* 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 DomainApplication application;
@Inject DomainAllocateFlow() {}
@Override
protected final void initDomainCreateOrAllocateFlow() {
registerExtensions(AllocateCreateExtension.class);

View file

@ -51,6 +51,8 @@ import google.registry.model.smd.EncodedSignedMark;
import java.util.List;
import javax.inject.Inject;
/**
* 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> {
@Inject DomainApplicationCreateFlow() {}
@Override
protected void initDomainCreateFlow() {
registerExtensions(FeeCreateExtension.class, LaunchCreateExtension.class);

View file

@ -36,6 +36,8 @@ import google.registry.model.reporting.HistoryEntry;
import java.util.Set;
import javax.inject.Inject;
/**
* An EPP flow that deletes a domain application.
*
@ -51,6 +53,8 @@ import java.util.Set;
public class DomainApplicationDeleteFlow
extends ResourceSyncDeleteFlow<DomainApplication, Builder, Delete> {
@Inject DomainApplicationDeleteFlow() {}
@Override
protected void initResourceCreateOrMutateFlow() throws EppException {
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.SignedMark;
import javax.inject.Inject;
/**
* An EPP flow that reads a domain application.
*
@ -45,6 +47,8 @@ public class DomainApplicationInfoFlow extends BaseDomainInfoFlow<DomainApplicat
private boolean includeMarks;
@Inject DomainApplicationInfoFlow() {}
@Override
protected final void initSingleResourceFlow() throws EppException {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* An EPP flow that updates a domain resource.
*
@ -59,6 +61,8 @@ import google.registry.model.reporting.HistoryEntry;
public class DomainApplicationUpdateFlow
extends BaseDomainUpdateFlow<DomainApplication, Builder> {
@Inject DomainApplicationUpdateFlow() {}
@Override
protected void initDomainUpdateFlow() throws EppException {
registerExtensions(LaunchUpdateExtension.class, SecDnsUpdateExtension.class);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -54,6 +54,8 @@ import google.registry.model.reporting.HistoryEntry;
import org.joda.money.Money;
import org.joda.time.DateTime;
import javax.inject.Inject;
/**
* 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 renewCost;
@Inject DomainRestoreRequestFlow() {}
@Override
protected final void initResourceCreateOrMutateFlow() throws EppException {
registerExtensions(FeeUpdateExtension.class, RgpUpdateExtension.class);

View file

@ -45,6 +45,8 @@ import google.registry.model.transfer.TransferData;
import org.joda.time.DateTime;
import javax.inject.Inject;
/**
* 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
ResourceTransferApproveFlow<DomainResource, Builder, Transfer> {
@Inject DomainTransferApproveFlow() {}
@Override
protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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
extends ResourceTransferCancelFlow<DomainResource, Builder, Transfer> {
@Inject DomainTransferCancelFlow() {}
/**
* 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

View file

@ -18,6 +18,8 @@ import google.registry.flows.ResourceTransferQueryFlow;
import google.registry.model.domain.DomainCommand.Transfer;
import google.registry.model.domain.DomainResource;
import javax.inject.Inject;
/**
* 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.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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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
extends ResourceTransferRejectFlow<DomainResource, Builder, Transfer> {
@Inject DomainTransferRejectFlow() {}
@Override
protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException {
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());

View file

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

View file

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

View file

@ -27,12 +27,17 @@ import google.registry.model.host.HostResource;
import java.util.Set;
import javax.inject.Inject;
/**
* An EPP flow that checks whether a host can be provisioned.
*
* @error {@link google.registry.flows.ResourceCheckFlow.TooManyResourceChecksException}
*/
public class HostCheckFlow extends ResourceCheckFlow<HostResource, Check> {
@Inject HostCheckFlow() {}
@Override
protected CheckData getCheckData() {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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;
@Inject HostCreateFlow() {}
@Override
protected void initResourceCreateOrMutateFlow() throws EppException {
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.reporting.HistoryEntry;
import javax.inject.Inject;
/**
* 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. */
private static final int FAILFAST_CHECK_COUNT = 5;
@Inject HostDeleteFlow() {}
@Override
protected boolean isLinkedForFailfast(final Ref<HostResource> ref) {
// 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.HostResource;
import javax.inject.Inject;
/**
* An EPP flow that reads a host.
*
* @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 javax.inject.Inject;
/**
* An EPP flow that updates a host resource.
*
@ -72,6 +74,8 @@ public class HostUpdateFlow extends ResourceUpdateFlow<HostResource, Builder, Up
private String newHostName;
private boolean isHostRename;
@Inject HostUpdateFlow() {}
@Override
protected void initResourceCreateOrMutateFlow() throws EppException {
String suppliedNewHostName = command.getInnerChange().getFullyQualifiedHostName();

View file

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

View file

@ -29,6 +29,8 @@ import google.registry.model.poll.PollMessage;
import java.util.List;
import javax.inject.Inject;
/**
* An EPP flow for requesting poll messages.
*
@ -36,6 +38,8 @@ import java.util.List;
*/
public class PollRequestFlow extends PollFlow {
@Inject PollRequestFlow() {}
@Override
public final EppOutput run() throws EppException {
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.Greeting;
import javax.inject.Inject;
/** A flow for an Epp "hello". */
public class HelloFlow extends Flow {
@Inject HelloFlow() {}
@Override
public EppOutput run() {
return EppOutput.create(Greeting.create(now));

View file

@ -41,6 +41,8 @@ import google.registry.util.FormattingLogger;
import java.util.Set;
import javax.inject.Inject;
/**
* An EPP flow for login.
*
@ -68,6 +70,8 @@ public class LoginFlow extends Flow {
/** Maximum number of failed login attempts allowed per connection. */
private static final int MAX_FAILED_LOGIN_ATTEMPTS_PER_CONNECTION = 3;
@Inject LoginFlow() {}
/** Run the flow and log errors. */
@Override
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.model.eppoutput.EppOutput;
import javax.inject.Inject;
/**
* An EPP flow for logout.
*
* @error {@link google.registry.flows.LoggedInFlow.NotLoggedInException}
*/
public class LogoutFlow extends LoggedInFlow {
@Inject LogoutFlow() {}
@Override
public final EppOutput run() throws EppException {
sessionMetadata.invalidate();

View file

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

View file

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

View file

@ -31,6 +31,7 @@ java_library(
"//java/com/google/common/net",
"//third_party/java/appengine:appengine-api-testonly",
"//third_party/java/appengine:appengine-testing",
"//third_party/java/dagger",
"//third_party/java/joda_money",
"//third_party/java/joda_time",
"//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.persistReservedList;
import static google.registry.testing.DatastoreHelper.persistResource;
import static org.mockito.Mockito.mock;
import com.google.common.collect.ImmutableSet;
import google.registry.config.RegistryEnvironment;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registry;
import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.util.SystemClock;
import org.json.simple.JSONValue;
import org.junit.Before;
@ -66,9 +65,11 @@ public class CheckApiActionTest {
action.domain = domain;
action.response = new FakeResponse();
action.config = RegistryEnvironment.UNITTEST.config();
action.eppController = new EppController();
action.eppController.clock = new SystemClock();
action.eppController.metrics = mock(EppMetrics.class);
action.eppController = DaggerEppTestComponent.builder()
.fakesAndMocksModule(new FakesAndMocksModule(new FakeClock()))
.build()
.startRequest()
.eppController();
action.run();
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;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableMap;
@ -44,7 +45,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
@Before
public void initTld() {
createTld("example");
createTlds("example", "tld");
}
/** Create the two administrative contacts and two hosts. */
@ -129,7 +130,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
"domain_create_response.xml",
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(
"domain_delete.xml",
"generic_success_action_pending_response.xml",
@ -338,7 +339,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-01T00:01:00Z"));
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-A-EXAMPLE-16-20"),
ImmutableMap.of("ID", "1-B-EXAMPLE-17-21"),
"poll_ack_response_empty.xml",
null,
DateTime.parse("2001-01-01T00:01:00Z"));
@ -350,7 +351,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-06T00:01:00Z"));
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-A-EXAMPLE-16-22"),
ImmutableMap.of("ID", "1-B-EXAMPLE-17-23"),
"poll_ack_response_empty.xml",
null,
DateTime.parse("2001-01-06T00:01:00Z"));
@ -366,7 +367,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-06T00:02:00Z"));
assertCommandAndResponse(
"poll_ack.xml",
ImmutableMap.of("ID", "1-A-EXAMPLE-16-21"),
ImmutableMap.of("ID", "1-B-EXAMPLE-17-22"),
"poll_ack_response_empty.xml",
null,
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 javax.servlet.http.HttpServletResponse.SC_OK;
import static org.joda.time.DateTimeZone.UTC;
import static org.mockito.Mockito.mock;
import com.google.common.net.MediaType;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.model.ofy.Ofy;
import google.registry.monitoring.whitebox.EppMetrics;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeHttpSession;
import google.registry.testing.FakeResponse;
@ -100,8 +99,7 @@ public class EppTestCase extends ShardableTestCase {
// When a session is invalidated, reset the sessionMetadata field.
super.invalidate();
EppTestCase.this.sessionMetadata = null;
}
};
}};
}
String actualOutput = executeXmlCommand(input);
assertXmlEqualsWithMessage(
@ -118,14 +116,16 @@ public class EppTestCase extends ShardableTestCase {
EppRequestHandler handler = new EppRequestHandler();
FakeResponse response = new FakeResponse();
handler.response = response;
handler.eppController = new EppController();
handler.eppController.clock = clock;
handler.eppController.metrics = mock(EppMetrics.class);
handler.eppController = DaggerEppTestComponent.builder()
.fakesAndMocksModule(new FakesAndMocksModule(clock))
.build()
.startRequest()
.eppController();
handler.executeEpp(
sessionMetadata,
credentials,
EppRequestSource.UNIT_TEST,
false,
false, // Not dryRun.
isSuperuser,
inputXml.getBytes(UTF_8));
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.Maps;
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
import google.registry.flows.picker.FlowPicker;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
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.ofy.Ofy;
import google.registry.model.poll.PollMessage;
@ -86,8 +85,6 @@ public abstract class FlowTestCase<F extends Flow> {
@Rule
public final InjectRule inject = new InjectRule();
private Class<? extends Flow> flowClass;
protected EppLoader eppLoader;
protected SessionMetadata sessionMetadata;
protected FakeClock clock = new FakeClock(DateTime.now(UTC));
@ -120,29 +117,8 @@ public abstract class FlowTestCase<F extends Flow> {
return readResourceUtf8(getClass(), "testdata/" + filename);
}
/** Load a flow from an epp object. */
private FlowRunner getFlowRunner(CommitMode commitMode, UserPrivileges userPrivileges)
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");
protected String getClientTrid() throws Exception {
return eppLoader.getEpp().getCommandWrapper().getClTrid();
}
/** 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 {
assertThat(getFlowRunner(CommitMode.LIVE, UserPrivileges.NORMAL).isTransactional())
.isEqualTo(isTransactional);
Class<? extends Flow> flowClass = FlowPicker.getFlowClass(eppLoader.getEpp());
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 {
@ -273,36 +256,67 @@ public abstract class FlowTestCase<F extends Flow> {
.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 {
EppOutput output = getFlowRunner(commitMode, userPrivileges).run();
EppOutput output = runFlowInternal(commitMode, userPrivileges);
marshal(output, ValidationMode.STRICT);
return output;
}
/** Shortcut to call {@link #runFlow(CommitMode, UserPrivileges)} as normal user and live run. */
public EppOutput runFlow() throws Exception {
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(
CommitMode commitMode, UserPrivileges userPrivileges, String xml, String... ignoredPaths)
throws Exception {
EppOutput eppOutput = getFlowRunner(commitMode, userPrivileges).run();
if (eppOutput.isResponse()) {
assertThat(eppOutput.isSuccess()).isTrue();
// Always ignore the server trid, since it's generated and meaningless to flow correctness.
String[] ignoredPathsPlusTrid = FluentIterable.from(ignoredPaths)
.append("epp.response.trID.svTRID")
.toArray(String.class);
EppOutput output = runFlowInternal(commitMode, userPrivileges);
if (output.isResponse()) {
assertThat(output.isSuccess()).isTrue();
}
try {
assertXmlEquals(
xml, new String(marshal(eppOutput, ValidationMode.STRICT), UTF_8), ignoredPaths);
xml, new String(marshal(output, ValidationMode.STRICT), UTF_8), ignoredPathsPlusTrid);
} catch (Throwable e) {
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.
throw new Exception(
String.format(
"Invalid xml.\nExpected:\n%s\n\nActual:\n%s\n",
xml,
marshal(eppOutput, ValidationMode.LENIENT)),
marshal(output, ValidationMode.LENIENT)),
e);
}
// 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()
.hasTransferGainingClientId("NewRegistrar").and()
.hasTransferLosingClientId("TheRegistrar").and()
.hasTransferRequestTrid(getTrid()).and()
.hasTransferRequestClientTrid(getClientTrid()).and()
.hasCurrentSponsorClientId("TheRegistrar").and()
.hasPendingTransferExpirationTime(afterTransfer).and()
.hasOnlyOneHistoryEntryWhich()

View file

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

View file

@ -3,7 +3,7 @@
<create>
<domain:create
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:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>

View file

@ -6,7 +6,7 @@
<resData>
<domain:creData
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:exDate>2002-06-01T00:02:00.0Z</domain:exDate>
</domain:creData>

View file

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

View file

@ -3,7 +3,7 @@
<info>
<domain:info
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>
</info>
<clTRID>ABC-12345</clTRID>

View file

@ -7,7 +7,7 @@
<resData>
<domain:infData
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:status s="inactive"/>
<domain:status s="pendingDelete"/>

View file

@ -3,7 +3,7 @@
<update>
<domain:update
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:update>
</update>

View file

@ -3,7 +3,7 @@
<result code="1301">
<msg>Command completed successfully; ack to dequeue</msg>
</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>
<msg>Transfer requested.</msg>
</msgQ>

View file

@ -3,7 +3,7 @@
<result code="1301">
<msg>Command completed successfully; ack to dequeue</msg>
</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>
<msg>Transfer approved.</msg>
</msgQ>

View file

@ -4,7 +4,7 @@
<result code="1301">
<msg>Command completed successfully; ack to dequeue</msg>
</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>
<msg>Transfer approved.</msg>
</msgQ>

View file

@ -15,35 +15,19 @@
package google.registry.model;
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.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTld;
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.persistResourceWithCommitLog;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
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.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 org.joda.time.DateTime;
@ -71,7 +55,6 @@ public class EppResourceUtilsTest {
public final InjectRule inject = new InjectRule();
private final FakeClock clock = new FakeClock(DateTime.now(UTC));
private EppLoader eppLoader;
@Before
public void init() throws Exception {
@ -79,103 +62,6 @@ public class EppResourceUtilsTest {
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
public void testLoadAtPointInTime_beforeCreated_returnsNull() throws Exception {
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.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.TruthChainer.And;
@ -205,10 +204,10 @@ abstract class AbstractEppResourceSubject
"has transferStatus");
}
public And<S> hasTransferRequestTrid(Trid trid) {
public And<S> hasTransferRequestClientTrid(String clTrid) {
return hasValue(
trid,
getSubject().getTransferData().getTransferRequestTrid(),
clTrid,
getSubject().getTransferData().getTransferRequestTrid().getClientTransactionId(),
"has trid");
}