Flatten the domain info flows

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=133955573
This commit is contained in:
cgoldfeder 2016-09-22 08:20:06 -07:00 committed by Ben McIlwain
parent 1b34f1e326
commit a69fc769af
6 changed files with 157 additions and 66 deletions

View file

@ -15,6 +15,7 @@
package google.registry.flows; package google.registry.flows;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.nullToEmpty;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ -22,6 +23,7 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException; import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
import google.registry.flows.picker.FlowPicker; import google.registry.flows.picker.FlowPicker;
import google.registry.model.domain.launch.ApplicationIdTargetExtension;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
@ -194,6 +196,15 @@ public class FlowModule {
return ((SingleResourceCommand) resourceCommand).getTargetId(); return ((SingleResourceCommand) resourceCommand).getTargetId();
} }
@Provides
@FlowScope
@ApplicationId
static String provideApplicationId(EppInput eppInput) {
// Treat a missing application id as empty so we can always inject a non-null value.
return nullToEmpty(
eppInput.getSingleExtension(ApplicationIdTargetExtension.class).getApplicationId());
}
@Provides @Provides
@FlowScope @FlowScope
@PollMessageId @PollMessageId
@ -254,6 +265,11 @@ public class FlowModule {
@Documented @Documented
public @interface TargetId {} public @interface TargetId {}
/** Dagger qualifier for the application id for domain application flows. */
@Qualifier
@Documented
public @interface ApplicationId {}
/** Dagger qualifier for the message id for poll flows. */ /** Dagger qualifier for the message id for poll flows. */
@Qualifier @Qualifier
@Documented @Documented

View file

@ -15,17 +15,29 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.EppXmlTransformer.unmarshal; import static google.registry.flows.EppXmlTransformer.unmarshal;
import static google.registry.flows.ResourceFlowUtils.loadResourceForQuery;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.verifyLaunchApplicationIdMatchesDomain; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent;
import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.ParameterValuePolicyErrorException;
import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.RequiredParameterMissingException;
import google.registry.flows.FlowModule.ApplicationId;
import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.LoggedInFlow;
import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainApplication;
import google.registry.model.domain.DomainApplication.Builder; import google.registry.model.domain.DomainCommand.Info;
import google.registry.model.domain.launch.LaunchInfoExtension; import google.registry.model.domain.launch.LaunchInfoExtension;
import google.registry.model.domain.launch.LaunchInfoResponseExtension; import google.registry.model.domain.launch.LaunchInfoResponseExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.mark.Mark; import google.registry.model.mark.Mark;
import google.registry.model.smd.EncodedSignedMark; import google.registry.model.smd.EncodedSignedMark;
@ -33,75 +45,86 @@ import google.registry.model.smd.SignedMark;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
* An EPP flow that reads a domain application. * An EPP flow that returns information about a domain application.
*
* <p>Only the registrar that owns the application can see its info. The flow can optionally include
* delegated hosts in its response.
* *
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
* @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException} * @error {@link google.registry.flows.exceptions.ResourceToQueryDoesNotExistException}
* @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException} * @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException}
* @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException} * @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException}
* @error {@link DomainApplicationInfoFlow.MissingApplicationIdException} * @error {@link MissingApplicationIdException}
*/ */
public class DomainApplicationInfoFlow extends BaseDomainInfoFlow<DomainApplication, Builder> { public final class DomainApplicationInfoFlow extends LoggedInFlow {
private boolean includeMarks;
@Inject ResourceCommand resourceCommand;
@Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId;
@Inject @TargetId String targetId;
@Inject @ApplicationId String applicationId;
@Inject DomainApplicationInfoFlow() {} @Inject DomainApplicationInfoFlow() {}
@Override @Override
protected final void initSingleResourceFlow() throws EppException { protected final void initLoggedInFlow() throws EppException {
registerExtensions(LaunchInfoExtension.class); registerExtensions(LaunchInfoExtension.class);
// We need to do this in init rather than verify or we'll get the generic "object not found". }
LaunchInfoExtension extension = eppInput.getSingleExtension(LaunchInfoExtension.class);
if (extension.getApplicationId() == null) { @Override
public final EppOutput run() throws EppException {
if (applicationId.isEmpty()) {
throw new MissingApplicationIdException(); throw new MissingApplicationIdException();
} }
includeMarks = Boolean.TRUE.equals(extension.getIncludeMark()); // Default to false. DomainApplication application =
} loadResourceForQuery(DomainApplication.class, applicationId, now);
verifyApplicationDomainMatchesTargetId(application, targetId);
@Override verifyOptionalAuthInfoForResource(authInfo, application);
protected final void verifyQueryIsAllowed() throws EppException { LaunchInfoExtension launchInfo = eppInput.getSingleExtension(LaunchInfoExtension.class);
verifyLaunchApplicationIdMatchesDomain(command, existingResource); if (!application.getPhase().equals(launchInfo.getPhase())) {
if (!existingResource.getPhase().equals(
eppInput.getSingleExtension(LaunchInfoExtension.class).getPhase())) {
throw new ApplicationLaunchPhaseMismatchException(); throw new ApplicationLaunchPhaseMismatchException();
} }
// We don't support authInfo for applications, so if it's another registrar always fail.
verifyResourceOwnership(clientId, application);
return createOutput(
SUCCESS,
getResourceInfo(application),
getDomainResponseExtensions(application, launchInfo));
} }
@Override DomainApplication getResourceInfo(DomainApplication application) {
protected final DomainApplication getResourceInfo() throws EppException { if (!((Info) resourceCommand).getHostsRequest().requestDelegated()) {
// We don't support authInfo for applications, so if it's another registrar always fail.
verifyResourceOwnership(getClientId(), existingResource);
if (!command.getHostsRequest().requestDelegated()) {
// Delegated hosts are present by default, so clear them out if they aren't wanted. // Delegated hosts are present by default, so clear them out if they aren't wanted.
// This requires overriding the implicit status values so that we don't get INACTIVE added due // This requires overriding the implicit status values so that we don't get INACTIVE added due
// to the missing nameservers. // to the missing nameservers.
return existingResource.asBuilder() return application.asBuilder()
.setNameservers(null) .setNameservers(null)
.buildWithoutImplicitStatusValues(); .buildWithoutImplicitStatusValues();
} }
return existingResource; return application;
} }
@Override ImmutableList<ResponseExtension> getDomainResponseExtensions(
protected final ImmutableList<? extends ResponseExtension> getDomainResponseExtensions() DomainApplication application, LaunchInfoExtension launchInfo) {
throws EppException {
ImmutableList.Builder<Mark> marksBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<Mark> marksBuilder = new ImmutableList.Builder<>();
if (includeMarks) { if (Boolean.TRUE.equals(launchInfo.getIncludeMark())) { // Default to false.
for (EncodedSignedMark encodedMark : existingResource.getEncodedSignedMarks()) { for (EncodedSignedMark encodedMark : application.getEncodedSignedMarks()) {
try { try {
marksBuilder.add(unmarshal(SignedMark.class, encodedMark.getBytes()).getMark()); marksBuilder.add(unmarshal(SignedMark.class, encodedMark.getBytes()).getMark());
} catch (EppException e) { } catch (EppException e) {
// This is a serious error; don't let the benign EppException propagate. // This is a serious error; don't let the benign EppException propagate.
throw new IllegalStateException("Could not decode a stored encoded signed mark"); throw new IllegalStateException("Could not decode a stored encoded signed mark", e);
} }
} }
} }
return ImmutableList.of(new LaunchInfoResponseExtension.Builder() ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>();
.setPhase(existingResource.getPhase()) extensions.add(new LaunchInfoResponseExtension.Builder()
.setApplicationId(existingResource.getForeignKey()) .setPhase(application.getPhase())
.setApplicationStatus(existingResource.getApplicationStatus()) .setApplicationId(application.getForeignKey())
.setApplicationStatus(application.getApplicationStatus())
.setMarks(marksBuilder.build()) .setMarks(marksBuilder.build())
.build()); .build());
addSecDnsExtensionIfPresent(extensions, application.getDsData());
return extensions.build();
} }
/** Application id is required. */ /** Application id is required. */

View file

@ -56,6 +56,7 @@ import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DesignatedContact.Type; import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.DomainApplication;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainCommand.CreateOrUpdate; import google.registry.model.domain.DomainCommand.CreateOrUpdate;
import google.registry.model.domain.DomainCommand.InvalidReferencesException; import google.registry.model.domain.DomainCommand.InvalidReferencesException;
@ -71,9 +72,11 @@ import google.registry.model.domain.fee.FeeTransformCommandExtension;
import google.registry.model.domain.launch.LaunchExtension; import google.registry.model.domain.launch.LaunchExtension;
import google.registry.model.domain.launch.LaunchPhase; import google.registry.model.domain.launch.LaunchPhase;
import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.secdns.SecDnsInfoExtension;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.mark.Mark; import google.registry.model.mark.Mark;
import google.registry.model.mark.ProtectedMark; import google.registry.model.mark.ProtectedMark;
@ -385,6 +388,14 @@ public class DomainFlowUtils {
} }
} }
/** Verifies that an application's domain name matches the target id (from a command). */
static void verifyApplicationDomainMatchesTargetId(
DomainApplication application, String targetId) throws EppException {
if (!application.getFullyQualifiedDomainName().equals(targetId)) {
throw new ApplicationDomainNameMismatchException();
}
}
/** /**
* Verifies that a domain name is allowed to be delegated to the given client id. The only case * Verifies that a domain name is allowed to be delegated to the given client id. The only case
* where it would not be allowed is if domain name is premium, and premium names are blocked by * where it would not be allowed is if domain name is premium, and premium names are blocked by
@ -738,6 +749,21 @@ public class DomainFlowUtils {
.build(); .build();
} }
/**
* Adds a secDns extension to a list if the given set of dsData is non-empty.
*
* <p>According to RFC 5910 section 2, we should only return this if the client specified the
* "urn:ietf:params:xml:ns:secDNS-1.1" when logging in. However, this is a "SHOULD" not a "MUST"
* and we are going to ignore it; clients who don't care about secDNS can just ignore it.
*/
static void addSecDnsExtensionIfPresent(
ImmutableList.Builder<ResponseExtension> extensions,
ImmutableSet<DelegationSignerData> dsData) {
if (!dsData.isEmpty()) {
extensions.add(SecDnsInfoExtension.create(dsData));
}
}
/** Encoded signed marks must use base64 encoding. */ /** Encoded signed marks must use base64 encoding. */
static class Base64RequiredForEncodedSignedMarksException static class Base64RequiredForEncodedSignedMarksException
extends ParameterValuePolicyErrorException { extends ParameterValuePolicyErrorException {

View file

@ -14,13 +14,23 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.ResourceFlowUtils.loadResourceForQuery;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfoForResource;
import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent;
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.util.CollectionUtils.forceEmptyToNull;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.FlowModule.ClientId;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.LoggedInFlow;
import google.registry.model.domain.DomainCommand.Info;
import google.registry.model.domain.DomainCommand.Info.HostsRequest;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.model.domain.DomainResource.Builder; import google.registry.model.domain.DomainResource.Builder;
import google.registry.model.domain.fee06.FeeInfoCommandExtensionV06; import google.registry.model.domain.fee06.FeeInfoCommandExtensionV06;
@ -28,50 +38,70 @@ import google.registry.model.domain.fee06.FeeInfoResponseExtensionV06;
import google.registry.model.domain.flags.FlagsInfoResponseExtension; import google.registry.model.domain.flags.FlagsInfoResponseExtension;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.rgp.RgpInfoExtension; import google.registry.model.domain.rgp.RgpInfoExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
* An EPP flow that reads a domain. * An EPP flow that returns information about a domain.
*
* <p>The registrar that owns the domain, and any registrar presenting a valid authInfo for the
* domain, will get a rich result with all of the domain's fields. All other requests will be
* answered with a minimal result containing only basic information about the domain.
* *
* @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
* @error {@link google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException} * @error {@link google.registry.flows.exceptions.ResourceToQueryDoesNotExistException}
* @error {@link DomainFlowUtils.BadPeriodUnitException} * @error {@link DomainFlowUtils.BadPeriodUnitException}
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException} * @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
* @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException} * @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException}
* @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException} * @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException}
*/ */
public class DomainInfoFlow extends BaseDomainInfoFlow<DomainResource, Builder> { public final class DomainInfoFlow extends LoggedInFlow {
@Inject Optional<AuthInfo> authInfo;
@Inject @ClientId String clientId;
@Inject @TargetId String targetId;
@Inject ResourceCommand resourceCommand;
@Inject DomainInfoFlow() {} @Inject DomainInfoFlow() {}
@Override @Override
protected void initSingleResourceFlow() throws EppException { protected void initLoggedInFlow() throws EppException {
registerExtensions(FeeInfoCommandExtensionV06.class); registerExtensions(FeeInfoCommandExtensionV06.class);
} }
@Override @Override
protected final DomainResource getResourceInfo() { public final EppOutput run() throws EppException {
DomainResource domain = loadResourceForQuery(DomainResource.class, targetId, now);
verifyOptionalAuthInfoForResource(authInfo, domain);
return createOutput(
SUCCESS,
getResourceInfo(domain),
getDomainResponseExtensions(domain));
}
private DomainResource getResourceInfo(DomainResource domain) {
// If authInfo is non-null, then the caller is authorized to see the full information since we // If authInfo is non-null, then the caller is authorized to see the full information since we
// will have already verified the authInfo is valid in ResourceQueryFlow.verifyIsAllowed(). // will have already verified the authInfo is valid.
if (!getClientId().equals(existingResource.getCurrentSponsorClientId()) if (!(clientId.equals(domain.getCurrentSponsorClientId()) || authInfo.isPresent())) {
&& command.getAuthInfo() == null) {
// Registrars can only see a few fields on unauthorized domains. // Registrars can only see a few fields on unauthorized domains.
// This is a policy decision that is left up to us by the rfcs. // This is a policy decision that is left up to us by the rfcs.
return new DomainResource.Builder() return new DomainResource.Builder()
.setFullyQualifiedDomainName(existingResource.getFullyQualifiedDomainName()) .setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName())
.setRepoId(existingResource.getRepoId()) .setRepoId(domain.getRepoId())
.setCurrentSponsorClientId(existingResource.getCurrentSponsorClientId()) .setCurrentSponsorClientId(domain.getCurrentSponsorClientId())
.setRegistrant(existingResource.getRegistrant()) .setRegistrant(domain.getRegistrant())
// If we didn't do this, we'd get implicit status values. // If we didn't do this, we'd get implicit status values.
.buildWithoutImplicitStatusValues(); .buildWithoutImplicitStatusValues();
} }
Builder info = existingResource.asBuilder(); HostsRequest hostsRequest = ((Info) resourceCommand).getHostsRequest();
if (!command.getHostsRequest().requestSubordinate()) { Builder info = domain.asBuilder();
if (!hostsRequest.requestSubordinate()) {
info.setSubordinateHosts(null); info.setSubordinateHosts(null);
} }
if (!command.getHostsRequest().requestDelegated()) { if (!hostsRequest.requestDelegated()) {
// Delegated hosts are present by default, so clear them out if they aren't wanted. // Delegated hosts are present by default, so clear them out if they aren't wanted.
// This requires overriding the implicit status values so that we don't get INACTIVE added due // This requires overriding the implicit status values so that we don't get INACTIVE added due
// to the missing nameservers. // to the missing nameservers.
@ -80,14 +110,11 @@ public class DomainInfoFlow extends BaseDomainInfoFlow<DomainResource, Builder>
return info.build(); return info.build();
} }
@Override private ImmutableList<ResponseExtension> getDomainResponseExtensions(DomainResource domain)
protected final ImmutableList<ResponseExtension> getDomainResponseExtensions()
throws EppException { throws EppException {
ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>(); ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>();
// According to RFC 5910 section 2, we should only return this if the client specified the addSecDnsExtensionIfPresent(extensions, domain.getDsData());
// "urn:ietf:params:xml:ns:rgp-1.0" when logging in. However, this is a "SHOULD" not a "MUST" ImmutableSet<GracePeriodStatus> gracePeriodStatuses = domain.getGracePeriodStatuses();
// and we are going to ignore it; clients who don't care about rgp can just ignore it.
ImmutableSet<GracePeriodStatus> gracePeriodStatuses = existingResource.getGracePeriodStatuses();
if (!gracePeriodStatuses.isEmpty()) { if (!gracePeriodStatuses.isEmpty()) {
extensions.add(RgpInfoExtension.create(gracePeriodStatuses)); extensions.add(RgpInfoExtension.create(gracePeriodStatuses));
} }
@ -98,8 +125,8 @@ public class DomainInfoFlow extends BaseDomainInfoFlow<DomainResource, Builder>
handleFeeRequest( handleFeeRequest(
feeInfo, feeInfo,
builder, builder,
InternetDomainName.from(getTargetId()), InternetDomainName.from(targetId),
getClientId(), clientId,
null, null,
feeInfo.getEffectiveDate().isPresent() ? feeInfo.getEffectiveDate().get() : now, feeInfo.getEffectiveDate().isPresent() ? feeInfo.getEffectiveDate().get() : now,
eppInput); eppInput);
@ -107,15 +134,14 @@ public class DomainInfoFlow extends BaseDomainInfoFlow<DomainResource, Builder>
} }
// If the TLD uses the flags extension, add it to the info response. // If the TLD uses the flags extension, add it to the info response.
Optional<RegistryExtraFlowLogic> extraLogicManager = Optional<RegistryExtraFlowLogic> extraLogicManager =
RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource); RegistryExtraFlowLogicProxy.newInstanceForDomain(domain);
if (extraLogicManager.isPresent()) { if (extraLogicManager.isPresent()) {
List<String> flags = extraLogicManager.get().getExtensionFlags( List<String> flags = extraLogicManager.get().getExtensionFlags(
existingResource, this.getClientId(), now); // As-of date is always now for info commands. domain, clientId, now); // As-of date is always now for info commands.
if (!flags.isEmpty()) { if (!flags.isEmpty()) {
extensions.add(FlagsInfoResponseExtension.create(flags)); extensions.add(FlagsInfoResponseExtension.create(flags));
} }
} }
return forceEmptyToNull(extensions.build());
return extensions.build();
} }
} }

View file

@ -28,10 +28,10 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException; import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import google.registry.flows.domain.DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException; import google.registry.flows.domain.DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException;
import google.registry.flows.domain.DomainApplicationInfoFlow.MissingApplicationIdException; import google.registry.flows.domain.DomainApplicationInfoFlow.MissingApplicationIdException;
import google.registry.flows.domain.DomainFlowUtils.ApplicationDomainNameMismatchException; import google.registry.flows.domain.DomainFlowUtils.ApplicationDomainNameMismatchException;
import google.registry.flows.exceptions.ResourceToQueryDoesNotExistException;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DesignatedContact.Type; import google.registry.model.domain.DesignatedContact.Type;

View file

@ -29,11 +29,11 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException;
import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException; import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException; import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException; import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException;
import google.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException; import google.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
import google.registry.flows.exceptions.ResourceToQueryDoesNotExistException;
import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.contact.ContactAuthInfo; import google.registry.model.contact.ContactAuthInfo;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;