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 e78da4aa8..e561f94a1 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java @@ -229,6 +229,15 @@ public final class DomainRenewFlow implements TransactionalFlow { private DomainHistory buildDomainHistory( DomainBase newDomain, DateTime now, Period period, Duration renewGracePeriod) { + Optional metadataExtensionOpt = + eppInput.getSingleExtension(MetadataExtension.class); + if (metadataExtensionOpt.isPresent()) { + MetadataExtension metadataExtension = metadataExtensionOpt.get(); + if (metadataExtension.getReason() != null) { + historyBuilder.setReason(metadataExtension.getReason()); + } + historyBuilder.setRequestedByRegistrar(metadataExtension.getRequestedByRegistrar()); + } return historyBuilder .setType(DOMAIN_RENEW) .setPeriod(period) diff --git a/core/src/main/java/google/registry/tools/RenewDomainCommand.java b/core/src/main/java/google/registry/tools/RenewDomainCommand.java index a7bd2fd82..233872189 100644 --- a/core/src/main/java/google/registry/tools/RenewDomainCommand.java +++ b/core/src/main/java/google/registry/tools/RenewDomainCommand.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.util.CollectionUtils.findDuplicates; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import com.beust.jcommander.Parameter; @@ -53,6 +54,17 @@ final class RenewDomainCommand extends MutatingEppToolCommand { @Parameter(description = "Names of the domains to renew.", required = true) private List mainParameters; + @Parameter( + names = {"--reason"}, + description = "Reason for the change.") + String reason; + + @Parameter( + names = {"--registrar_request"}, + description = "Whether the change was requested by a registrar.", + arity = 1) + Boolean requestedByRegistrar; + @Inject Clock clock; @@ -70,12 +82,23 @@ final class RenewDomainCommand extends MutatingEppToolCommand { checkArgumentPresent(domainOptional, "Domain '%s' does not exist or is deleted", domainName); setSoyTemplate(DomainRenewSoyInfo.getInstance(), DomainRenewSoyInfo.RENEWDOMAIN); DomainBase domain = domainOptional.get(); - addSoyRecord( - isNullOrEmpty(clientId) ? domain.getCurrentSponsorRegistrarId() : clientId, + + SoyMapData soyMapData = new SoyMapData( "domainName", domain.getDomainName(), "expirationDate", domain.getRegistrationExpirationTime().toString(DATE_FORMATTER), - "period", String.valueOf(period))); + "period", String.valueOf(period)); + + if (requestedByRegistrar != null) { + soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); + } + if (reason != null) { + checkArgumentNotNull( + requestedByRegistrar, "--registrar_request is required when --reason is specified"); + soyMapData.put("reason", reason); + } + addSoyRecord( + isNullOrEmpty(clientId) ? domain.getCurrentSponsorRegistrarId() : clientId, soyMapData); } } } diff --git a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java index 391ec1a02..8b965b0c0 100644 --- a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -163,7 +163,12 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { .toString(DateTimeFormat.forPattern("YYYY-MM-dd")), // period is the number of years to renew the registration for "period", - String.valueOf(1))); + String.valueOf(1), + // use the same values for reason and requestedByRegistrar from update flow + "reason", + (undo ? "Undo " : "") + "Uniform Rapid Suspension", + "requestedByRegistrar", + Boolean.toString(false))); } // trigger update flow diff --git a/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy b/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy index e0ec5c53d..27ebaca6b 100644 --- a/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy +++ b/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy @@ -21,6 +21,8 @@ {@param domainName: string} {@param expirationDate: string} {@param period: string} +{@param? reason: string} +{@param? requestedByRegistrar: string} @@ -32,6 +34,18 @@ {$period} + {if $reason or $requestedByRegistrar} + + + {if $reason} + {$reason} + {/if} + {if $requestedByRegistrar} + {$requestedByRegistrar} + {/if} + + + {/if} RegistryTool diff --git a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java index 24444e52c..535da6767 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java @@ -42,6 +42,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import google.registry.flows.EppException; +import google.registry.flows.EppRequestSource; import google.registry.flows.FlowUtils.UnknownCurrencyEppException; import google.registry.flows.ResourceFlowTestCase; import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException; @@ -533,6 +534,55 @@ class DomainRenewFlowTest extends ResourceFlowTestCase runCommand("--period 4")); } + + @Test + void testFailure_registrarRequestIsRequiredWhenReasonIsPresent() { + persistActiveDomain( + "domain.tld", + DateTime.parse("2014-09-05T05:05:05Z"), + DateTime.parse("2015-09-05T05:05:05Z")); + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> runCommandForced("domain.tld", "--period=1", "--reason=testing_only")); + assertThat(e) + .hasMessageThat() + .isEqualTo("--registrar_request is required when --reason is specified"); + } } diff --git a/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java b/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java index 73f473f91..56770d668 100644 --- a/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java +++ b/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java @@ -242,13 +242,44 @@ class UniformRapidSuspensionCommandTest } @Test - void testRenewOneYear_renewFlowIsTriggered() throws Exception { - // this test case was written based on an existing test case, - // testUndo_removesLocksReplacesHostsAndDsData() but two things were modified to test the - // renew workflow, which were: - // 1) a different domain that contains creation time and expiration time - // 2) renew_one_year is set to true + void testRenewOneYearWithoutUndo_verifyReasonWithoutUndo() throws Exception { + persistDomainWithHosts( + newDomainBase("evil.tld") + .asBuilder() + .setCreationTimeForTest(DateTime.parse("2021-10-01T05:01:11Z")) + .setRegistrationExpirationTime(DateTime.parse("2022-10-01T05:01:11Z")) + .setPersistedCurrentSponsorRegistrarId("CharlestonRoad") + .build(), + defaultDsData, + urs1, + urs2); + runCommandForced( + "--domain_name=evil.tld", + "--hosts=ns1.example.com,ns2.example.com", + "--renew_one_year=true"); + + eppVerifier + .expectRegistrarId("CharlestonRoad") + .expectSuperuser() + .verifySent( + "domain_renew_via_urs.xml", + ImmutableMap.of( + "DOMAIN", + "evil.tld", + "EXPDATE", + "2022-10-01", + "YEARS", + "1", + "REASON", + "Uniform Rapid Suspension", + "REQUESTED", + "false")) + .verifySentAny(); + } + + @Test + void testRenewOneYearWithUndo_verifyReasonWithUndo() throws Exception { persistDomainWithHosts( newDomainBase("evil.tld") .asBuilder() @@ -266,21 +297,65 @@ class UniformRapidSuspensionCommandTest "--hosts=ns1.example.com,ns2.example.com", "--renew_one_year=true"); - // verify if renew flow is triggered eppVerifier .expectRegistrarId("CharlestonRoad") .expectSuperuser() .verifySent( - "domain_renew.xml", - ImmutableMap.of("DOMAIN", "evil.tld", "EXPDATE", "2022-10-01", "YEARS", "1")); + "domain_renew_via_urs.xml", + ImmutableMap.of( + "DOMAIN", + "evil.tld", + "EXPDATE", + "2022-10-01", + "YEARS", + "1", + "REASON", + "Undo Uniform Rapid Suspension", + "REQUESTED", + "false")) + .verifySentAny(); + } + + @Test + void testRenewOneYear_verifyBothRenewAndUpdateFlowsAreTriggered() throws Exception { + persistDomainWithHosts( + newDomainBase("evil.tld") + .asBuilder() + .setCreationTimeForTest(DateTime.parse("2021-10-01T05:01:11Z")) + .setRegistrationExpirationTime(DateTime.parse("2022-10-01T05:01:11Z")) + .setPersistedCurrentSponsorRegistrarId("CharlestonRoad") + .build(), + defaultDsData, + urs1, + urs2); + + runCommandForced( + "--domain_name=evil.tld", + "--undo", + "--hosts=ns1.example.com,ns2.example.com", + "--renew_one_year=true"); - // verify if update flow is triggered eppVerifier .expectRegistrarId("CharlestonRoad") .expectSuperuser() - .verifySent("uniform_rapid_suspension_undo.xml") - .verifyNoMoreSent(); - assertNotInStdout("--undo"); // Undo shouldn't print a new undo command. + .verifySent( + "domain_renew_via_urs.xml", + ImmutableMap.of( + "DOMAIN", + "evil.tld", + "EXPDATE", + "2022-10-01", + "YEARS", + "1", + "REASON", + "Undo Uniform Rapid Suspension", + "REQUESTED", + "false")); + + eppVerifier + .expectRegistrarId("CharlestonRoad") + .expectSuperuser() + .verifySent("uniform_rapid_suspension_undo.xml"); // verify that no other flows are triggered after the renew and update flows eppVerifier.verifyNoMoreSent(); diff --git a/core/src/test/resources/google/registry/flows/domain/domain_renew_metadata_with_reason_and_requestedByRegistrar.xml b/core/src/test/resources/google/registry/flows/domain/domain_renew_metadata_with_reason_and_requestedByRegistrar.xml new file mode 100644 index 000000000..8a87e692c --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_renew_metadata_with_reason_and_requestedByRegistrar.xml @@ -0,0 +1,19 @@ + + + + + %DOMAIN% + %EXPDATE% + %YEARS% + + + + + %REASON% + %REQUESTED% + + + ABC-12345 + + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_renew_metadata_with_requestedByRegistrar_only.xml b/core/src/test/resources/google/registry/flows/domain/domain_renew_metadata_with_requestedByRegistrar_only.xml new file mode 100644 index 000000000..0db09aa18 --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_renew_metadata_with_requestedByRegistrar_only.xml @@ -0,0 +1,18 @@ + + + + + example.tld + 2000-04-03 + 1 + + + + + true + + + ABC-12345 + + diff --git a/core/src/test/resources/google/registry/tools/server/domain_renew_via_urs.xml b/core/src/test/resources/google/registry/tools/server/domain_renew_via_urs.xml new file mode 100644 index 000000000..c56b0bb29 --- /dev/null +++ b/core/src/test/resources/google/registry/tools/server/domain_renew_via_urs.xml @@ -0,0 +1,19 @@ + + + + + %DOMAIN% + %EXPDATE% + %YEARS% + + + + + %REASON% + %REQUESTED% + + + RegistryTool + + diff --git a/core/src/test/resources/google/registry/tools/server/domain_renew_with_metadata_requestedByRegistrar_only.xml b/core/src/test/resources/google/registry/tools/server/domain_renew_with_metadata_requestedByRegistrar_only.xml new file mode 100644 index 000000000..760978616 --- /dev/null +++ b/core/src/test/resources/google/registry/tools/server/domain_renew_with_metadata_requestedByRegistrar_only.xml @@ -0,0 +1,18 @@ + + + + + %DOMAIN% + %EXPDATE% + %YEARS% + + + + + %REQUESTED% + + + RegistryTool + +