diff --git a/core/src/main/java/google/registry/module/RequestComponent.java b/core/src/main/java/google/registry/module/RequestComponent.java
index 8c819e936..bef4e282c 100644
--- a/core/src/main/java/google/registry/module/RequestComponent.java
+++ b/core/src/main/java/google/registry/module/RequestComponent.java
@@ -109,7 +109,6 @@ import google.registry.tools.server.RefreshDnsForAllDomainsAction;
import google.registry.tools.server.ToolsServerModule;
import google.registry.tools.server.UpdateUserGroupAction;
import google.registry.tools.server.VerifyOteAction;
-import google.registry.ui.server.console.ConsoleBulkDomainAction;
import google.registry.ui.server.console.ConsoleDomainGetAction;
import google.registry.ui.server.console.ConsoleDomainListAction;
import google.registry.ui.server.console.ConsoleDumDownloadAction;
@@ -122,6 +121,7 @@ import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
import google.registry.ui.server.console.ConsoleUserDataAction;
import google.registry.ui.server.console.ConsoleUsersAction;
import google.registry.ui.server.console.RegistrarsAction;
+import google.registry.ui.server.console.domains.ConsoleBulkDomainAction;
import google.registry.ui.server.console.settings.ContactAction;
import google.registry.ui.server.console.settings.SecurityAction;
import google.registry.ui.server.console.settings.WhoisRegistrarFieldsAction;
diff --git a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java
index 3960f8f30..0728e4624 100644
--- a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java
+++ b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java
@@ -25,7 +25,6 @@ import google.registry.monitoring.whitebox.WhiteboxModule;
import google.registry.request.RequestComponentBuilder;
import google.registry.request.RequestModule;
import google.registry.request.RequestScope;
-import google.registry.ui.server.console.ConsoleBulkDomainAction;
import google.registry.ui.server.console.ConsoleDomainGetAction;
import google.registry.ui.server.console.ConsoleDomainListAction;
import google.registry.ui.server.console.ConsoleDumDownloadAction;
@@ -38,6 +37,7 @@ import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
import google.registry.ui.server.console.ConsoleUserDataAction;
import google.registry.ui.server.console.ConsoleUsersAction;
import google.registry.ui.server.console.RegistrarsAction;
+import google.registry.ui.server.console.domains.ConsoleBulkDomainAction;
import google.registry.ui.server.console.settings.ContactAction;
import google.registry.ui.server.console.settings.SecurityAction;
import google.registry.ui.server.console.settings.WhoisRegistrarFieldsAction;
diff --git a/core/src/main/java/google/registry/tools/EppToolCommand.java b/core/src/main/java/google/registry/tools/EppToolCommand.java
index c3bdcf69b..fcc22130f 100644
--- a/core/src/main/java/google/registry/tools/EppToolCommand.java
+++ b/core/src/main/java/google/registry/tools/EppToolCommand.java
@@ -136,7 +136,7 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo
params.put("dryRun", dryRun);
params.put("clientId", command.clientId);
params.put("superuser", superuser);
- params.put("xml", URLEncoder.encode(command.xml, UTF_8.toString()));
+ params.put("xml", URLEncoder.encode(command.xml, UTF_8));
String requestBody =
Joiner.on('&').withKeyValueSeparator("=").join(filterValues(params, Objects::nonNull));
responses.add(
diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleBulkDomainAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleBulkDomainAction.java
deleted file mode 100644
index 375171272..000000000
--- a/core/src/main/java/google/registry/ui/server/console/ConsoleBulkDomainAction.java
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2024 The Nomulus 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.ui.server.console;
-
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
-import static jakarta.servlet.http.HttpServletResponse.SC_OK;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.escape.Escaper;
-import com.google.common.xml.XmlEscapers;
-import com.google.gson.JsonElement;
-import com.google.gson.annotations.Expose;
-import google.registry.flows.EppController;
-import google.registry.flows.EppRequestSource;
-import google.registry.flows.PasswordOnlyTransportCredentials;
-import google.registry.flows.StatelessRequestSessionMetadata;
-import google.registry.model.console.ConsolePermission;
-import google.registry.model.console.User;
-import google.registry.model.eppcommon.ProtocolDefinition;
-import google.registry.model.eppoutput.EppOutput;
-import google.registry.model.eppoutput.Result;
-import google.registry.request.Action;
-import google.registry.request.OptionalJsonPayload;
-import google.registry.request.Parameter;
-import google.registry.request.auth.Auth;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import javax.inject.Inject;
-
-/**
- * Console endpoint to perform the same action to a list of domains.
- *
- *
All requests must include the {@link BulkAction} to perform as well as a {@link
- * BulkDomainList} of domains on which to apply the action. The remaining contents of the request
- * body depend on the type of action -- some requests may require more data than others.
- */
-@Action(
- service = Action.GaeService.DEFAULT,
- gkeService = Action.GkeService.CONSOLE,
- path = ConsoleBulkDomainAction.PATH,
- method = Action.Method.POST,
- auth = Auth.AUTH_PUBLIC_LOGGED_IN)
-public class ConsoleBulkDomainAction extends ConsoleApiAction {
-
- public static final String PATH = "/console-api/bulk-domain";
-
- private static Escaper XML_ESCAPER = XmlEscapers.xmlContentEscaper();
-
- public enum BulkAction {
- DELETE,
- SUSPEND
- }
-
- /** All requests must include at least a list of domain names on which to perform the action. */
- public record BulkDomainList(@Expose List domainList) {}
-
- public record BulkDomainDeleteRequest(@Expose String reason) {}
-
- public record BulkDomainSuspendRequest(@Expose String reason) {}
-
- private static final String DOMAIN_DELETE_XML =
- """
-
-
-
-
-
- %DOMAIN_NAME%
-
-
-
-
- %REASON%
- true
-
-
- RegistryConsole
-
-""";
-
- private static final String DOMAIN_SUSPEND_XML =
- """
-
-
-
-
-
- %DOMAIN_NAME%
-
-
-
-
-
-
-
-
-
-
-
-
- Console suspension: %REASON%
- false
-
-
- RegistryTool
-
-""";
-
- private final EppController eppController;
- private final String registrarId;
- private final String bulkDomainAction;
- private final Optional optionalJsonPayload;
-
- @Inject
- public ConsoleBulkDomainAction(
- ConsoleApiParams consoleApiParams,
- EppController eppController,
- @Parameter("registrarId") String registrarId,
- @Parameter("bulkDomainAction") String bulkDomainAction,
- @OptionalJsonPayload Optional optionalJsonPayload) {
- super(consoleApiParams);
- this.eppController = eppController;
- this.registrarId = registrarId;
- this.bulkDomainAction = bulkDomainAction;
- this.optionalJsonPayload = optionalJsonPayload;
- }
-
- @Override
- protected void postHandler(User user) {
- // Temporary flag while testing
- if (!user.getUserRoles().isAdmin()) {
- consoleApiParams.response().setStatus(SC_FORBIDDEN);
- return;
- }
- BulkAction bulkAction = BulkAction.valueOf(bulkDomainAction);
- JsonElement jsonPayload =
- optionalJsonPayload.orElseThrow(
- () -> new IllegalArgumentException("Bulk action payload must be present"));
- BulkDomainList domainList = consoleApiParams.gson().fromJson(jsonPayload, BulkDomainList.class);
- checkPermission(user, registrarId, ConsolePermission.EXECUTE_EPP_COMMANDS);
- ImmutableMap result =
- switch (bulkAction) {
- case DELETE -> handleBulkDelete(jsonPayload, domainList, user);
- case SUSPEND -> handleBulkSuspend(jsonPayload, domainList, user);
- };
- // Front end should parse situations where only some commands worked
- consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(result));
- consoleApiParams.response().setStatus(SC_OK);
- }
-
- private ImmutableMap handleBulkDelete(
- JsonElement jsonPayload, BulkDomainList domainList, User user) {
- String reason =
- consoleApiParams.gson().fromJson(jsonPayload, BulkDomainDeleteRequest.class).reason;
- return runCommandOverDomains(
- domainList,
- DOMAIN_DELETE_XML,
- new ImmutableMap.Builder().put("REASON", reason),
- user);
- }
-
- private ImmutableMap handleBulkSuspend(
- JsonElement jsonPayload, BulkDomainList domainList, User user) {
- String reason =
- consoleApiParams.gson().fromJson(jsonPayload, BulkDomainSuspendRequest.class).reason;
- return runCommandOverDomains(
- domainList,
- DOMAIN_SUSPEND_XML,
- new ImmutableMap.Builder().put("REASON", reason),
- user);
- }
-
- /** Runs the provided XML template and substitutions over a provided list of domains. */
- private ImmutableMap runCommandOverDomains(
- BulkDomainList domainList,
- String xmlTemplate,
- ImmutableMap.Builder replacements,
- User user) {
- return domainList.domainList.stream()
- .collect(
- toImmutableMap(
- d -> d,
- d ->
- executeEpp(
- fillSubstitutions(xmlTemplate, replacements.put("DOMAIN_NAME", d)), user)));
- }
-
- private ConsoleEppOutput executeEpp(String xml, User user) {
- return ConsoleEppOutput.fromEppOutput(
- eppController.handleEppCommand(
- new StatelessRequestSessionMetadata(
- registrarId, ProtocolDefinition.getVisibleServiceExtensionUris()),
- new PasswordOnlyTransportCredentials(),
- EppRequestSource.CONSOLE,
- false,
- user.getUserRoles().isAdmin(),
- xml.getBytes(UTF_8)));
- }
-
- /** Fills the provided XML template with the replacement values, including escaping the values. */
- private String fillSubstitutions(
- String xmlTemplate, ImmutableMap.Builder replacements) {
- String xml = xmlTemplate;
- for (Map.Entry entry : replacements.buildKeepingLast().entrySet()) {
- xml = xml.replaceAll("%" + entry.getKey() + "%", XML_ESCAPER.escape(entry.getValue()));
- }
- return xml;
- }
-
- public record ConsoleEppOutput(@Expose String message, @Expose int responseCode) {
- static ConsoleEppOutput fromEppOutput(EppOutput eppOutput) {
- Result result = eppOutput.getResponse().getResult();
- return new ConsoleEppOutput(result.getMsg(), result.getCode().code);
- }
- }
-}
diff --git a/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java
new file mode 100644
index 000000000..3d9a05ecc
--- /dev/null
+++ b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java
@@ -0,0 +1,125 @@
+// Copyright 2024 The Nomulus 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.ui.server.console.domains;
+
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static jakarta.servlet.http.HttpServletResponse.SC_OK;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonElement;
+import com.google.gson.annotations.Expose;
+import google.registry.flows.EppController;
+import google.registry.flows.EppRequestSource;
+import google.registry.flows.PasswordOnlyTransportCredentials;
+import google.registry.flows.StatelessRequestSessionMetadata;
+import google.registry.model.console.User;
+import google.registry.model.eppcommon.ProtocolDefinition;
+import google.registry.model.eppoutput.EppOutput;
+import google.registry.model.eppoutput.Result;
+import google.registry.request.Action;
+import google.registry.request.OptionalJsonPayload;
+import google.registry.request.Parameter;
+import google.registry.request.auth.Auth;
+import google.registry.ui.server.console.ConsoleApiAction;
+import google.registry.ui.server.console.ConsoleApiParams;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+
+/**
+ * Console endpoint to perform the same action to a list of domains.
+ *
+ * All requests must include the {@link ConsoleDomainActionType.BulkAction} to perform as well as
+ * a {@link BulkDomainList} of domains on which to apply the action. The remaining contents of the
+ * request body depend on the type of action -- some requests may require more data than others.
+ */
+@Action(
+ service = Action.GaeService.DEFAULT,
+ gkeService = Action.GkeService.CONSOLE,
+ path = ConsoleBulkDomainAction.PATH,
+ method = Action.Method.POST,
+ auth = Auth.AUTH_PUBLIC_LOGGED_IN)
+public class ConsoleBulkDomainAction extends ConsoleApiAction {
+
+ public static final String PATH = "/console-api/bulk-domain";
+
+ /** All requests must include at least a list of domain names on which to perform the action. */
+ public record BulkDomainList(@Expose List domainList) {}
+
+ private final EppController eppController;
+ private final String registrarId;
+ private final String bulkDomainAction;
+ private final Optional optionalJsonPayload;
+
+ @Inject
+ public ConsoleBulkDomainAction(
+ ConsoleApiParams consoleApiParams,
+ EppController eppController,
+ @Parameter("registrarId") String registrarId,
+ @Parameter("bulkDomainAction") String bulkDomainAction,
+ @OptionalJsonPayload Optional optionalJsonPayload) {
+ super(consoleApiParams);
+ this.eppController = eppController;
+ this.registrarId = registrarId;
+ this.bulkDomainAction = bulkDomainAction;
+ this.optionalJsonPayload = optionalJsonPayload;
+ }
+
+ @Override
+ protected void postHandler(User user) {
+ // Temporary flag while testing
+ if (!user.getUserRoles().isAdmin()) {
+ consoleApiParams.response().setStatus(SC_FORBIDDEN);
+ return;
+ }
+ JsonElement jsonPayload =
+ optionalJsonPayload.orElseThrow(
+ () -> new IllegalArgumentException("Bulk action payload must be present"));
+ BulkDomainList domainList = consoleApiParams.gson().fromJson(jsonPayload, BulkDomainList.class);
+ ConsoleDomainActionType actionType =
+ ConsoleDomainActionType.parseActionType(bulkDomainAction, jsonPayload);
+
+ checkPermission(user, registrarId, actionType.getNecessaryPermission());
+
+ ImmutableMap result =
+ domainList.domainList.stream()
+ .collect(
+ toImmutableMap(d -> d, d -> executeEpp(actionType.getXmlContentsToRun(d), user)));
+ // Front end should parse situations where only some commands worked
+ consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(result));
+ consoleApiParams.response().setStatus(SC_OK);
+ }
+
+ private ConsoleEppOutput executeEpp(String xml, User user) {
+ return ConsoleEppOutput.fromEppOutput(
+ eppController.handleEppCommand(
+ new StatelessRequestSessionMetadata(
+ registrarId, ProtocolDefinition.getVisibleServiceExtensionUris()),
+ new PasswordOnlyTransportCredentials(),
+ EppRequestSource.CONSOLE,
+ false,
+ user.getUserRoles().isAdmin(),
+ xml.getBytes(UTF_8)));
+ }
+
+ public record ConsoleEppOutput(@Expose String message, @Expose int responseCode) {
+ static ConsoleEppOutput fromEppOutput(EppOutput eppOutput) {
+ Result result = eppOutput.getResponse().getResult();
+ return new ConsoleEppOutput(result.getMsg(), result.getCode().code);
+ }
+ }
+}
diff --git a/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainDeleteActionType.java b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainDeleteActionType.java
new file mode 100644
index 000000000..1e05fa17d
--- /dev/null
+++ b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainDeleteActionType.java
@@ -0,0 +1,61 @@
+// Copyright 2025 The Nomulus 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.ui.server.console.domains;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonElement;
+import google.registry.model.console.ConsolePermission;
+
+/** An action that will run a delete EPP command on the given domain. */
+public class ConsoleBulkDomainDeleteActionType implements ConsoleDomainActionType {
+
+ private static final String DOMAIN_DELETE_XML =
+ """
+
+
+
+
+
+ %DOMAIN_NAME%
+
+
+
+
+ %REASON%
+ true
+
+
+ RegistryConsole
+
+""";
+
+ private final String reason;
+
+ public ConsoleBulkDomainDeleteActionType(JsonElement jsonElement) {
+ this.reason = jsonElement.getAsJsonObject().get("reason").getAsString();
+ }
+
+ @Override
+ public String getXmlContentsToRun(String domainName) {
+ return ConsoleDomainActionType.fillSubstitutions(
+ DOMAIN_DELETE_XML, ImmutableMap.of("DOMAIN_NAME", domainName, "REASON", reason));
+ }
+
+ @Override
+ public ConsolePermission getNecessaryPermission() {
+ return ConsolePermission.EXECUTE_EPP_COMMANDS;
+ }
+}
diff --git a/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainSuspendActionType.java b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainSuspendActionType.java
new file mode 100644
index 000000000..12c646313
--- /dev/null
+++ b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainSuspendActionType.java
@@ -0,0 +1,71 @@
+// Copyright 2025 The Nomulus 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.ui.server.console.domains;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonElement;
+import google.registry.model.console.ConsolePermission;
+
+/** An action that will suspend the given domain, assigning all 5 server*Prohibited statuses. */
+public class ConsoleBulkDomainSuspendActionType implements ConsoleDomainActionType {
+
+ private static final String DOMAIN_SUSPEND_XML =
+ """
+
+
+
+
+
+ %DOMAIN_NAME%
+
+
+
+
+
+
+
+
+
+
+
+
+ Console suspension: %REASON%
+ false
+
+
+ RegistryConsole
+
+""";
+
+ private final String reason;
+
+ public ConsoleBulkDomainSuspendActionType(JsonElement jsonElement) {
+ this.reason = jsonElement.getAsJsonObject().get("reason").getAsString();
+ }
+
+ @Override
+ public String getXmlContentsToRun(String domainName) {
+ return ConsoleDomainActionType.fillSubstitutions(
+ DOMAIN_SUSPEND_XML, ImmutableMap.of("DOMAIN_NAME", domainName, "REASON", reason));
+ }
+
+ @Override
+ public ConsolePermission getNecessaryPermission() {
+ return ConsolePermission.SUSPEND_DOMAIN;
+ }
+}
diff --git a/core/src/main/java/google/registry/ui/server/console/domains/ConsoleDomainActionType.java b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleDomainActionType.java
new file mode 100644
index 000000000..0f49c11a7
--- /dev/null
+++ b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleDomainActionType.java
@@ -0,0 +1,69 @@
+// Copyright 2025 The Nomulus 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.ui.server.console.domains;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.escape.Escaper;
+import com.google.common.xml.XmlEscapers;
+import com.google.gson.JsonElement;
+import google.registry.model.console.ConsolePermission;
+import java.util.Map;
+
+/**
+ * A type of EPP action to perform on domain(s), run by the {@link ConsoleBulkDomainAction}.
+ *
+ * Each {@link BulkAction} defines the class that implements that action, including the EPP XML
+ * that will be run and the permission required.
+ */
+public interface ConsoleDomainActionType {
+
+ enum BulkAction {
+ DELETE(ConsoleBulkDomainDeleteActionType.class),
+ SUSPEND(ConsoleBulkDomainSuspendActionType.class);
+
+ private final Class extends ConsoleDomainActionType> actionClass;
+
+ BulkAction(Class extends ConsoleDomainActionType> actionClass) {
+ this.actionClass = actionClass;
+ }
+
+ public Class extends ConsoleDomainActionType> getActionClass() {
+ return actionClass;
+ }
+ }
+
+ Escaper XML_ESCAPER = XmlEscapers.xmlContentEscaper();
+
+ static String fillSubstitutions(String xmlTemplate, ImmutableMap replacements) {
+ String xml = xmlTemplate;
+ for (Map.Entry entry : replacements.entrySet()) {
+ xml = xml.replaceAll("%" + entry.getKey() + "%", XML_ESCAPER.escape(entry.getValue()));
+ }
+ return xml;
+ }
+
+ String getXmlContentsToRun(String domainName);
+
+ ConsolePermission getNecessaryPermission();
+
+ static ConsoleDomainActionType parseActionType(String bulkDomainAction, JsonElement jsonElement) {
+ BulkAction bulkAction = BulkAction.valueOf(bulkDomainAction);
+ try {
+ return bulkAction.getActionClass().getConstructor(JsonElement.class).newInstance(jsonElement);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e); // shouldn't happen
+ }
+ }
+}
diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleBulkDomainActionTest.java b/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java
similarity index 95%
rename from core/src/test/java/google/registry/ui/server/console/ConsoleBulkDomainActionTest.java
rename to core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java
index d368fcccb..a299c95cc 100644
--- a/core/src/test/java/google/registry/ui/server/console/ConsoleBulkDomainActionTest.java
+++ b/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package google.registry.ui.server.console;
+package google.registry.ui.server.console.domains;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
@@ -49,6 +49,7 @@ import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
+import google.registry.ui.server.console.ConsoleApiParams;
import java.util.Optional;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -166,13 +167,13 @@ public class ConsoleBulkDomainActionTest {
@Test
void testFailure_badActionString() {
- ConsoleBulkDomainAction action = createAction("bad", null);
+ ConsoleBulkDomainAction action = createAction("bad", GSON.toJsonTree(ImmutableMap.of()));
action.run();
assertThat(fakeResponse.getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(fakeResponse.getPayload())
.isEqualTo(
"No enum constant"
- + " google.registry.ui.server.console.ConsoleBulkDomainAction.BulkAction.bad");
+ + " google.registry.ui.server.console.domains.ConsoleDomainActionType.BulkAction.bad");
}
@Test
@@ -186,7 +187,8 @@ public class ConsoleBulkDomainActionTest {
@Test
void testFailure_noPermission() {
JsonElement payload =
- GSON.toJsonTree(ImmutableMap.of("domainList", ImmutableList.of("domain.tld")));
+ GSON.toJsonTree(
+ ImmutableMap.of("domainList", ImmutableList.of("domain.tld"), "reason", "reason"));
ConsoleBulkDomainAction action =
createAction(
"DELETE",
diff --git a/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt b/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt
index 4bd9f2ff5..4470947da 100644
--- a/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt
+++ b/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt
@@ -14,4 +14,4 @@ CONSOLE /console-api/settings/contacts ContactAction GET,
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
CONSOLE /console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
-CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
\ No newline at end of file
+CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
diff --git a/core/src/test/resources/google/registry/module/routing.txt b/core/src/test/resources/google/registry/module/routing.txt
index a616f9c60..4b8a6a77c 100644
--- a/core/src/test/resources/google/registry/module/routing.txt
+++ b/core/src/test/resources/google/registry/module/routing.txt
@@ -80,4 +80,4 @@ CONSOLE /console-api/settings/contacts ContactAction
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
CONSOLE /console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
-CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
\ No newline at end of file
+CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC