From 52ef8592fcc1bf484a0b64c87e28e757461aa82f Mon Sep 17 00:00:00 2001 From: gbrodman Date: Wed, 24 Nov 2021 14:57:44 -0500 Subject: [PATCH] Provide useful error messages on flows run during read-only mode (#1425) We want to keep the read-only-mode-exception as an unchecked exception, so we introduce a temporary check in the EppController that provides a specific error message for this situation (rather than letting it fall through to the generic "command failed" messaging --- .../google/registry/flows/EppController.java | 5 +-- .../google/registry/flows/EppException.java | 17 ++++++++- .../google/registry/flows/FlowRunner.java | 4 ++ .../flows/contact/ContactCreateFlow.java | 1 + .../flows/contact/ContactDeleteFlow.java | 1 + .../contact/ContactTransferApproveFlow.java | 1 + .../contact/ContactTransferCancelFlow.java | 1 + .../contact/ContactTransferRejectFlow.java | 1 + .../contact/ContactTransferRequestFlow.java | 1 + .../flows/contact/ContactUpdateFlow.java | 1 + .../flows/domain/DomainCreateFlow.java | 1 + .../flows/domain/DomainDeleteFlow.java | 1 + .../flows/domain/DomainRenewFlow.java | 1 + .../domain/DomainRestoreRequestFlow.java | 3 +- .../domain/DomainTransferApproveFlow.java | 1 + .../domain/DomainTransferCancelFlow.java | 1 + .../domain/DomainTransferRejectFlow.java | 1 + .../domain/DomainTransferRequestFlow.java | 4 +- .../flows/domain/DomainUpdateFlow.java | 1 + .../registry/flows/host/HostCreateFlow.java | 1 + .../registry/flows/host/HostDeleteFlow.java | 1 + .../registry/flows/host/HostUpdateFlow.java | 5 ++- .../TransactionManagerFactory.java | 2 +- .../flows/contact/ContactCreateFlowTest.java | 11 ++++++ .../flows/contact/ContactDeleteFlowTest.java | 12 ++++++ .../ContactTransferApproveFlowTest.java | 11 ++++++ .../ContactTransferCancelFlowTest.java | 11 ++++++ .../ContactTransferRejectFlowTest.java | 11 ++++++ .../ContactTransferRequestFlowTest.java | 11 ++++++ .../flows/contact/ContactUpdateFlowTest.java | 12 ++++++ .../flows/domain/DomainCreateFlowTest.java | 12 ++++++ .../flows/domain/DomainDeleteFlowTest.java | 12 ++++++ .../flows/domain/DomainRenewFlowTest.java | 19 ++++++++++ .../domain/DomainRestoreRequestFlowTest.java | 12 ++++++ .../domain/DomainTransferApproveFlowTest.java | 13 ++++++- .../domain/DomainTransferCancelFlowTest.java | 13 ++++++- .../domain/DomainTransferRejectFlowTest.java | 13 ++++++- .../domain/DomainTransferRequestFlowTest.java | 16 +++++++- .../flows/domain/DomainUpdateFlowTest.java | 15 +++++++- .../flows/host/HostCreateFlowTest.java | 11 ++++++ .../flows/host/HostDeleteFlowTest.java | 12 ++++++ .../flows/host/HostUpdateFlowTest.java | 13 +++++++ docs/flows.md | 38 +++++++++++++++++++ 43 files changed, 319 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/google/registry/flows/EppController.java b/core/src/main/java/google/registry/flows/EppController.java index 1ef1c015c..51008c0f3 100644 --- a/core/src/main/java/google/registry/flows/EppController.java +++ b/core/src/main/java/google/registry/flows/EppController.java @@ -143,9 +143,6 @@ public final class EppController { /** Creates a response indicating an EPP failure. */ @VisibleForTesting static EppOutput getErrorResponse(Result result, Trid trid) { - return EppOutput.create(new EppResponse.Builder() - .setResult(result) - .setTrid(trid) - .build()); + return EppOutput.create(new EppResponse.Builder().setResult(result).setTrid(trid).build()); } } diff --git a/core/src/main/java/google/registry/flows/EppException.java b/core/src/main/java/google/registry/flows/EppException.java index 6ef2dd3fe..c4dd987f8 100644 --- a/core/src/main/java/google/registry/flows/EppException.java +++ b/core/src/main/java/google/registry/flows/EppException.java @@ -25,10 +25,12 @@ import google.registry.model.eppinput.EppInput.InnerCommand; import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; import google.registry.model.eppoutput.Result; import google.registry.model.eppoutput.Result.Code; +import google.registry.persistence.transaction.TransactionManagerFactory.ReadOnlyModeException; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import javax.annotation.Nullable; /** Exception used to propagate all failures containing one or more EPP responses. */ public abstract class EppException extends Exception { @@ -37,7 +39,12 @@ public abstract class EppException extends Exception { /** Create an EppException with a custom message. */ private EppException(String message) { - super(message); + this(message, null); + } + + /** Create an EppException with a custom message and cause. */ + private EppException(String message, @Nullable Throwable cause) { + super(message, cause); Code code = getClass().getAnnotation(EppResultCode.class).value(); Preconditions.checkState(!code.isSuccess()); this.result = Result.create(code, message); @@ -255,4 +262,12 @@ public abstract class EppException extends Exception { super("Specified protocol version is not implemented"); } } + + /** Registry is currently undergoing maintenance and is in read-only mode. */ + @EppResultCode(Code.COMMAND_FAILED) + public static class ReadOnlyModeEppException extends EppException { + ReadOnlyModeEppException(ReadOnlyModeException cause) { + super("Registry is currently undergoing maintenance and is in read-only mode", cause); + } + } } diff --git a/core/src/main/java/google/registry/flows/FlowRunner.java b/core/src/main/java/google/registry/flows/FlowRunner.java index e1859ba7c..d7747fc98 100644 --- a/core/src/main/java/google/registry/flows/FlowRunner.java +++ b/core/src/main/java/google/registry/flows/FlowRunner.java @@ -19,6 +19,7 @@ import static google.registry.xml.XmlTransformer.prettyPrint; import com.google.common.base.Strings; import com.google.common.flogger.FluentLogger; +import google.registry.flows.EppException.ReadOnlyModeEppException; import google.registry.flows.FlowModule.DryRun; import google.registry.flows.FlowModule.InputXml; import google.registry.flows.FlowModule.RegistrarId; @@ -28,6 +29,7 @@ import google.registry.flows.session.LoginFlow; import google.registry.model.eppcommon.Trid; import google.registry.model.eppoutput.EppOutput; import google.registry.monitoring.whitebox.EppMetric; +import google.registry.persistence.transaction.TransactionManagerFactory.ReadOnlyModeException; import javax.inject.Inject; import javax.inject.Provider; @@ -97,6 +99,8 @@ public class FlowRunner { return e.output; } catch (EppRuntimeException e) { throw e.getCause(); + } catch (ReadOnlyModeException e) { + throw new ReadOnlyModeEppException(e); } } diff --git a/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java b/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java index c8931935b..a249d9226 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactCreateFlow.java @@ -50,6 +50,7 @@ import org.joda.time.DateTime; /** * An EPP flow that creates a new contact. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link ResourceAlreadyExistsForThisClientException} * @error {@link ResourceCreateContentionException} * @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException} diff --git a/core/src/main/java/google/registry/flows/contact/ContactDeleteFlow.java b/core/src/main/java/google/registry/flows/contact/ContactDeleteFlow.java index b2129a546..6e7133368 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactDeleteFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactDeleteFlow.java @@ -60,6 +60,7 @@ import org.joda.time.DateTime; * references to the host before the deletion is allowed to proceed. A poll message will be written * with the success or failure message when the process is complete. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} diff --git a/core/src/main/java/google/registry/flows/contact/ContactTransferApproveFlow.java b/core/src/main/java/google/registry/flows/contact/ContactTransferApproveFlow.java index 6b28d61fd..32cae77fd 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactTransferApproveFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactTransferApproveFlow.java @@ -54,6 +54,7 @@ import org.joda.time.DateTime; * transfer is automatically approved. Within that window, this flow allows the losing client to * explicitly approve the transfer request, which then becomes effective immediately. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} diff --git a/core/src/main/java/google/registry/flows/contact/ContactTransferCancelFlow.java b/core/src/main/java/google/registry/flows/contact/ContactTransferCancelFlow.java index 16382ed68..3c1cfcc24 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactTransferCancelFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactTransferCancelFlow.java @@ -54,6 +54,7 @@ import org.joda.time.DateTime; * transfer is automatically approved. Within that window, this flow allows the gaining client to * withdraw the transfer request. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException} diff --git a/core/src/main/java/google/registry/flows/contact/ContactTransferRejectFlow.java b/core/src/main/java/google/registry/flows/contact/ContactTransferRejectFlow.java index 30173c80b..cb2428ddb 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactTransferRejectFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactTransferRejectFlow.java @@ -53,6 +53,7 @@ import org.joda.time.DateTime; * transfer is automatically approved. Within that window, this flow allows the losing client to * reject the transfer request. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/flows/contact/ContactTransferRequestFlow.java b/core/src/main/java/google/registry/flows/contact/ContactTransferRequestFlow.java index 163caa97a..97833699d 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactTransferRequestFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactTransferRequestFlow.java @@ -63,6 +63,7 @@ import org.joda.time.Duration; * by the losing registrar or rejected, and the gaining registrar can also cancel the transfer * request. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.exceptions.AlreadyPendingTransferException} diff --git a/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java b/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java index b93e15b2e..71080c1d5 100644 --- a/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/contact/ContactUpdateFlow.java @@ -55,6 +55,7 @@ import org.joda.time.DateTime; /** * An EPP flow that updates a contact. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java index ec4c55f61..d15e2bd79 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java @@ -133,6 +133,7 @@ import org.joda.time.Duration; * google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException} * @error {@link * google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException} + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.exceptions.OnlyToolCanPassMetadataException} * @error {@link ResourceAlreadyExistsForThisClientException} * @error {@link ResourceCreateContentionException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java b/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java index c74de57e9..1190da927 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java @@ -103,6 +103,7 @@ import org.joda.time.Duration; /** * An EPP flow that deletes a domain. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java index e561f94a1..363a1e0f3 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java @@ -94,6 +94,7 @@ import org.joda.time.Duration; * longer than 10 years unless it comes in at the exact millisecond that the domain would have * expired. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.FlowUtils.UnknownCurrencyEppException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java index 2ad97eb6e..f23d58a75 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRestoreRequestFlow.java @@ -93,6 +93,7 @@ import org.joda.time.DateTime; * restored to a single year expiration starting at the restore time, regardless of what the * original expiration time was. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException} * @error {@link google.registry.flows.FlowUtils.UnknownCurrencyEppException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} @@ -110,7 +111,7 @@ import org.joda.time.DateTime; * @error {@link DomainRestoreRequestFlow.RestoreCommandIncludesChangesException} */ @ReportingSpec(ActivityReportField.DOMAIN_RGP_RESTORE_REQUEST) -public final class DomainRestoreRequestFlow implements TransactionalFlow { +public final class DomainRestoreRequestFlow implements TransactionalFlow { @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java index d4cacca10..a313151f7 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferApproveFlow.java @@ -78,6 +78,7 @@ import org.joda.time.DateTime; * timestamps such that they only would become active when the transfer period passed. In this flow, * those speculative objects are deleted and replaced with new ones with the correct approval time. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferCancelFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferCancelFlow.java index eb8e1f326..4cdf94c52 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferCancelFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferCancelFlow.java @@ -65,6 +65,7 @@ import org.joda.time.DateTime; * timestamps such that they only would become active when the transfer period passed. In this flow, * those speculative objects are deleted. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.exceptions.NotPendingTransferException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java index 8428c0ff0..fd334826c 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRejectFlow.java @@ -67,6 +67,7 @@ import org.joda.time.DateTime; * timestamps such that they only would become active when the transfer period passed. In this flow, * those speculative objects are deleted. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 9e3f591bf..775191ccc 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -93,6 +93,7 @@ import org.joda.time.DateTime; * rejection or cancellation of the request, they will be deleted (and in the approval case, * replaced with new ones with the correct approval time). * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.FlowUtils.UnknownCurrencyEppException} * @error {@link google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} @@ -102,7 +103,8 @@ import org.joda.time.DateTime; * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} * @error {@link google.registry.flows.exceptions.TransferPeriodMustBeOneYearException} * @error {@link InvalidTransferPeriodValueException} - * @error {@link google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException} + * @error {@link + * google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException} * @error {@link DomainFlowUtils.BadPeriodUnitException} * @error {@link DomainFlowUtils.CurrencyUnitMismatchException} * @error {@link DomainFlowUtils.CurrencyValueScaleException} diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java index 07af4c385..b788770b5 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -97,6 +97,7 @@ import org.joda.time.DateTime; * superuser. As such, adding or removing these statuses incurs a billing event. There will be only * one charge per update, even if several such statuses are updated at once. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException} * @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} diff --git a/core/src/main/java/google/registry/flows/host/HostCreateFlow.java b/core/src/main/java/google/registry/flows/host/HostCreateFlow.java index 6155ba6c3..a272e4c2c 100644 --- a/core/src/main/java/google/registry/flows/host/HostCreateFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostCreateFlow.java @@ -65,6 +65,7 @@ import org.joda.time.DateTime; * hosts cannot have any. This flow allows creating a host name, and if necessary enqueues tasks to * update DNS. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.FlowUtils.IpAddressVersionMismatchException} * @error {@link ResourceAlreadyExistsForThisClientException} * @error {@link ResourceCreateContentionException} diff --git a/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java b/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java index f5188fb4a..46359a2bf 100644 --- a/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java @@ -58,6 +58,7 @@ import org.joda.time.DateTime; * references to the host before the deletion is allowed to proceed. A poll message will be written * with the success or failure message when the process is complete. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} * @error {@link google.registry.flows.exceptions.ResourceStatusProhibitsOperationException} diff --git a/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java b/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java index 69f8029ef..5045b68db 100644 --- a/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java @@ -73,11 +73,12 @@ import org.joda.time.DateTime; * hosts. Internal hosts must have at least one IP address associated with them, whereas external * hosts cannot have any. * - *

This flow allows changing a host name, and adding or removing IP addresses to hosts. When - * a host is renamed from internal to external all IP addresses must be simultaneously removed, and + *

This flow allows changing a host name, and adding or removing IP addresses to hosts. When a + * host is renamed from internal to external all IP addresses must be simultaneously removed, and * when it is renamed from external to internal at least one must be added. If the host is renamed * or IP addresses are added, tasks are enqueued to update DNS accordingly. * + * @error {@link google.registry.flows.EppException.ReadOnlyModeEppException} * @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException} * @error {@link google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException} diff --git a/core/src/main/java/google/registry/persistence/transaction/TransactionManagerFactory.java b/core/src/main/java/google/registry/persistence/transaction/TransactionManagerFactory.java index 84f71be0c..4f053abff 100644 --- a/core/src/main/java/google/registry/persistence/transaction/TransactionManagerFactory.java +++ b/core/src/main/java/google/registry/persistence/transaction/TransactionManagerFactory.java @@ -157,7 +157,7 @@ public class TransactionManagerFactory { /** Registry is currently undergoing maintenance and is in read-only mode. */ public static class ReadOnlyModeException extends IllegalStateException { - ReadOnlyModeException() { + public ReadOnlyModeException() { super("Registry is currently undergoing maintenance and is in read-only mode"); } } diff --git a/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java b/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java index 1571cd893..239bee1dc 100644 --- a/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/contact/ContactCreateFlowTest.java @@ -26,15 +26,18 @@ import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptio import static org.junit.jupiter.api.Assertions.assertThrows; import google.registry.flows.EppException; +import google.registry.flows.EppException.ReadOnlyModeEppException; import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException; import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException; import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException; import google.registry.flows.exceptions.ResourceCreateContentionException; import google.registry.model.contact.ContactResource; +import google.registry.testing.DatabaseHelper; import google.registry.testing.DualDatabaseTest; import google.registry.testing.ReplayExtension; import google.registry.testing.TestOfyAndSql; +import google.registry.testing.TestOfyOnly; import org.joda.time.DateTime; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; @@ -137,4 +140,12 @@ class ContactCreateFlowTest extends ResourceFlowTestCase doFailingTest("domain_transfer_request.xml")); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + DatabaseHelper.removeDatabaseMigrationSchedule(); + } } diff --git a/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java index 57986c1fd..971d780ff 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java @@ -59,6 +59,7 @@ import com.google.common.collect.ImmutableSortedMap; import com.googlecode.objectify.Key; import google.registry.config.RegistryConfig; import google.registry.flows.EppException; +import google.registry.flows.EppException.ReadOnlyModeEppException; import google.registry.flows.EppException.UnimplementedExtensionException; import google.registry.flows.EppRequestSource; import google.registry.flows.ResourceFlowTestCase; @@ -101,9 +102,11 @@ import google.registry.model.host.HostResource; import google.registry.model.poll.PollMessage; import google.registry.model.tld.Registry; import google.registry.persistence.VKey; +import google.registry.testing.DatabaseHelper; import google.registry.testing.DualDatabaseTest; import google.registry.testing.ReplayExtension; import google.registry.testing.TestOfyAndSql; +import google.registry.testing.TestOfyOnly; import java.util.Optional; import org.joda.money.Money; import org.joda.time.DateTime; @@ -133,7 +136,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase