mirror of
https://github.com/google/nomulus
synced 2026-04-25 18:51:40 +00:00
Add console DUM download (#2402)
* Add console DUM download * Add console DUM download
This commit is contained in:
@@ -203,6 +203,17 @@ public final class RegistryConfig {
|
||||
return config.registrarConsole.announcementsEmailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The DUM file name, used as a file name base for DUM csv file
|
||||
*
|
||||
* @see google.registry.ui.server.console.ConsoleDumDownloadAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("dumFileName")
|
||||
public static String provideDumFileName(RegistryConfigSettings config) {
|
||||
return config.registrarConsole.dumFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The contact phone number. Used in the "contact-us" section of the registrar console.
|
||||
*
|
||||
|
||||
@@ -185,6 +185,7 @@ public class RegistryConfigSettings {
|
||||
|
||||
/** Configuration for the web-based registrar console. */
|
||||
public static class RegistrarConsole {
|
||||
public String dumFileName;
|
||||
public String logoFilename;
|
||||
public String supportPhoneNumber;
|
||||
public String supportEmailAddress;
|
||||
|
||||
@@ -396,6 +396,9 @@ rde:
|
||||
sshIdentityEmailAddress: rde@example.com
|
||||
|
||||
registrarConsole:
|
||||
# DUM download file name, excluding the extension
|
||||
dumFileName: dum_file_name
|
||||
|
||||
# Filename of the logo to use in the header of the console. This filename is
|
||||
# relative to ui/assets/images/
|
||||
logoFilename: logo.png
|
||||
|
||||
@@ -110,6 +110,7 @@ import google.registry.tools.server.ToolsServerModule;
|
||||
import google.registry.tools.server.VerifyOteAction;
|
||||
import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleDumDownloadAction;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
@@ -189,6 +190,8 @@ interface RequestComponent {
|
||||
|
||||
ConsoleUserDataAction consoleUserDataAction();
|
||||
|
||||
ConsoleDumDownloadAction ConsoleDumDownloadAction();
|
||||
|
||||
ContactAction contactAction();
|
||||
|
||||
CopyDetailReportsAction copyDetailReportAction();
|
||||
|
||||
@@ -27,6 +27,7 @@ import google.registry.request.RequestModule;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleDumDownloadAction;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
@@ -67,6 +68,8 @@ public interface FrontendRequestComponent {
|
||||
|
||||
ConsoleUserDataAction consoleUserDataAction();
|
||||
|
||||
ConsoleDumDownloadAction ConsoleDumDownloadAction();
|
||||
|
||||
ContactAction contactAction();
|
||||
|
||||
EppTlsAction eppTlsAction();
|
||||
|
||||
@@ -17,6 +17,8 @@ package google.registry.request;
|
||||
import com.google.common.net.MediaType;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -59,4 +61,6 @@ public interface Response {
|
||||
* @see HttpServletResponse#addCookie(Cookie)
|
||||
*/
|
||||
void addCookie(Cookie cookie);
|
||||
|
||||
PrintWriter getWriter() throws IOException;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.common.net.MediaType;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -64,4 +65,9 @@ public final class ResponseImpl implements Response {
|
||||
public void addCookie(Cookie cookie) {
|
||||
rsp.addCookie(cookie);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getWriter() throws IOException {
|
||||
return rsp.getWriter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
// 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.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleDumDownloadAction.PATH,
|
||||
method = {GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleDumDownloadAction extends ConsoleApiAction {
|
||||
|
||||
private static final String SQL_TEMPLATE =
|
||||
"""
|
||||
SELECT CONCAT(
|
||||
d.domain_name,',',d.creation_time,',',d.registration_expiration_time,',',d.statuses
|
||||
) AS result FROM "Domain" d
|
||||
WHERE d.current_sponsor_registrar_id = :registrarId
|
||||
AND d.deletion_time > ':now'
|
||||
AND d.creation_time <= ':now';
|
||||
""";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public static final String PATH = "/console-api/dum-download";
|
||||
private Clock clock;
|
||||
private final String registrarId;
|
||||
private final String dumFileName;
|
||||
|
||||
@Inject
|
||||
public ConsoleDumDownloadAction(
|
||||
Clock clock,
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrarId") String registrarId,
|
||||
@Config("dumFileName") String dumFileName) {
|
||||
super(consoleApiParams);
|
||||
this.registrarId = registrarId;
|
||||
this.clock = clock;
|
||||
this.dumFileName = dumFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
if (!user.getUserRoles().hasPermission(registrarId, ConsolePermission.DOWNLOAD_DOMAINS)) {
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
consoleApiParams.response().setContentType(MediaType.CSV_UTF_8);
|
||||
consoleApiParams
|
||||
.response()
|
||||
.setHeader(
|
||||
"Content-Disposition", String.format("attachment; filename=%s.csv", dumFileName));
|
||||
consoleApiParams
|
||||
.response()
|
||||
.setHeader("Cache-Control", "max-age=86400"); // 86400 seconds = 1 day
|
||||
consoleApiParams
|
||||
.response()
|
||||
.setDateHeader("Expires", DateTime.now(UTC).withTimeAtStartOfDay().plusDays(1));
|
||||
|
||||
try (var writer = consoleApiParams.response().getWriter()) {
|
||||
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT);
|
||||
writeCsv(csvPrinter);
|
||||
} catch (IOException e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
String.format("Failed to create DUM csv for %s", registrarId));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void writeCsv(CSVPrinter printer) throws IOException {
|
||||
String sql = SQL_TEMPLATE.replaceAll(":now", clock.nowUtc().toString());
|
||||
|
||||
// We deliberately don't want to use ImmutableList.copyOf because underlying list may contain
|
||||
// large amount of records and that will degrade performance.
|
||||
List<String> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(sql)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
|
||||
ImmutableList<String[]> formattedRecords =
|
||||
queryResult.stream().map(r -> r.split(",")).collect(toImmutableList());
|
||||
printer.printRecord(
|
||||
ImmutableList.of("Domain Name", "Creation Time", "Expiration Time", "Domain Statuses"));
|
||||
printer.printRecords(formattedRecords);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user