SEBSERV-191 gui implementation and download streaming
This commit is contained in:
parent
32d8c8a978
commit
06ce72a76f
20 changed files with 396 additions and 74 deletions
|
@ -24,6 +24,8 @@ import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
|
||||||
/** Global Constants used in SEB Server web-service as well as in web-gui component */
|
/** Global Constants used in SEB Server web-service as well as in web-gui component */
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
|
|
||||||
|
public static final String FILE_EXT_CSV = ".csv";
|
||||||
|
|
||||||
public static final String DEFAULT_LANG_CODE = "en";
|
public static final String DEFAULT_LANG_CODE = "en";
|
||||||
public static final String DEFAULT_TIME_ZONE_CODE = "UTC";
|
public static final String DEFAULT_TIME_ZONE_CODE = "UTC";
|
||||||
public static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip";
|
public static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip";
|
||||||
|
|
|
@ -199,6 +199,8 @@ public final class API {
|
||||||
public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search";
|
public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search";
|
||||||
public static final String SEB_CLIENT_EVENT_EXPORT_PATH_SEGMENT = "/export";
|
public static final String SEB_CLIENT_EVENT_EXPORT_PATH_SEGMENT = "/export";
|
||||||
public static final String SEB_CLIENT_EVENT_EXPORT_TYPE = "exportType";
|
public static final String SEB_CLIENT_EVENT_EXPORT_TYPE = "exportType";
|
||||||
|
public static final String SEB_CLIENT_EVENT_EXPORT_INCLUDE_CONNECTIONS = "includeConnectionDetails";
|
||||||
|
public static final String SEB_CLIENT_EVENT_EXPORT_INCLUDE_EXAMS = "includeExamDetails";
|
||||||
public static final String SEB_CLIENT_EVENT_EXTENDED_PAGE_ENDPOINT = SEB_CLIENT_EVENT_ENDPOINT
|
public static final String SEB_CLIENT_EVENT_EXTENDED_PAGE_ENDPOINT = SEB_CLIENT_EVENT_ENDPOINT
|
||||||
+ SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT;
|
+ SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT;
|
||||||
|
|
||||||
|
|
|
@ -262,6 +262,13 @@ public final class Result<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result<T> onSuccess(final Consumer<T> handler) {
|
||||||
|
if (this.error == null) {
|
||||||
|
handler.accept(this.value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Uses a given error handler to apply an error if there is one and returning itself again
|
/** Uses a given error handler to apply an error if there is one and returning itself again
|
||||||
* for further processing.
|
* for further processing.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.content;
|
package ch.ethz.seb.sebserver.gui.content;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -15,6 +16,8 @@ import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.tomcat.util.buf.StringUtils;
|
import org.apache.tomcat.util.buf.StringUtils;
|
||||||
|
import org.eclipse.rap.rwt.RWT;
|
||||||
|
import org.eclipse.rap.rwt.client.service.UrlLauncher;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -24,12 +27,15 @@ import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
@ -42,6 +48,8 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder;
|
import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.download.SEBClientLogExport;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetClientEventNames;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetClientEventNames;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||||
|
@ -84,23 +92,29 @@ public class SEBClientEvents implements TemplateComposer {
|
||||||
private final ResourceService resourceService;
|
private final ResourceService resourceService;
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
private final I18nSupport i18nSupport;
|
private final I18nSupport i18nSupport;
|
||||||
|
private final DownloadService downloadService;
|
||||||
private final SEBClientEventDetailsPopup sebClientEventDetailsPopup;
|
private final SEBClientEventDetailsPopup sebClientEventDetailsPopup;
|
||||||
private final SEBClientEventDeletePopup sebClientEventDeletePopup;
|
private final SEBClientEventDeletePopup sebClientEventDeletePopup;
|
||||||
private final int pageSize;
|
private final int pageSize;
|
||||||
|
private final String exportFileName;
|
||||||
|
|
||||||
public SEBClientEvents(
|
public SEBClientEvents(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
|
final DownloadService downloadService,
|
||||||
final SEBClientEventDetailsPopup sebClientEventDetailsPopup,
|
final SEBClientEventDetailsPopup sebClientEventDetailsPopup,
|
||||||
final SEBClientEventDeletePopup sebClientEventDeletePopup,
|
final SEBClientEventDeletePopup sebClientEventDeletePopup,
|
||||||
|
@Value("${sebserver.gui.seb.client.logs.export.filename:SEBClientLogs}") final String exportFileName,
|
||||||
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
|
this.downloadService = downloadService;
|
||||||
this.resourceService = pageService.getResourceService();
|
this.resourceService = pageService.getResourceService();
|
||||||
this.restService = this.resourceService.getRestService();
|
this.restService = this.resourceService.getRestService();
|
||||||
this.i18nSupport = this.resourceService.getI18nSupport();
|
this.i18nSupport = this.resourceService.getI18nSupport();
|
||||||
this.sebClientEventDetailsPopup = sebClientEventDetailsPopup;
|
this.sebClientEventDetailsPopup = sebClientEventDetailsPopup;
|
||||||
this.sebClientEventDeletePopup = sebClientEventDeletePopup;
|
this.sebClientEventDeletePopup = sebClientEventDeletePopup;
|
||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
|
this.exportFileName = exportFileName;
|
||||||
|
|
||||||
this.examFilter = new TableFilterAttribute(
|
this.examFilter = new TableFilterAttribute(
|
||||||
CriteriaType.SINGLE_SELECTION,
|
CriteriaType.SINGLE_SELECTION,
|
||||||
|
@ -219,12 +233,52 @@ public class SEBClientEvents implements TemplateComposer {
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
.publish(false)
|
.publish(false)
|
||||||
|
|
||||||
|
.newAction(ActionDefinition.LOGS_SEB_CLIENT_EXPORT_CSV)
|
||||||
|
.withExec(action -> this.exportLogs(action, ExportType.CSV, table))
|
||||||
|
.noEventPropagation()
|
||||||
|
.publishIf(() -> writeGrant, table.hasAnyContent())
|
||||||
|
|
||||||
.newAction(ActionDefinition.LOGS_SEB_CLIENT_DELETE_ALL)
|
.newAction(ActionDefinition.LOGS_SEB_CLIENT_DELETE_ALL)
|
||||||
.withExec(action -> this.getOpenDelete(action, table.getFilterCriteria()))
|
.withExec(action -> this.getOpenDelete(action, table.getFilterCriteria()))
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
.publishIf(() -> writeGrant, table.hasAnyContent());
|
.publishIf(() -> writeGrant, table.hasAnyContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PageAction exportLogs(
|
||||||
|
final PageAction action,
|
||||||
|
final ExportType type,
|
||||||
|
final EntityTable<ExtendedClientEvent> table) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
|
||||||
|
final String fileName = this.exportFileName
|
||||||
|
+ Constants.UNDERLINE
|
||||||
|
+ this.i18nSupport.formatDisplayDate(Utils.getMillisecondsNow())
|
||||||
|
.replace(" ", "_")
|
||||||
|
.replace(".", "_")
|
||||||
|
+ Constants.FILE_EXT_CSV;
|
||||||
|
final Map<String, String> queryAttrs = new HashMap<>();
|
||||||
|
|
||||||
|
queryAttrs.put(API.SEB_CLIENT_EVENT_EXPORT_TYPE, type.name());
|
||||||
|
final String sortAttr = table.getSortOrder().encode(table.getSortColumn());
|
||||||
|
queryAttrs.put(Page.ATTR_SORT, sortAttr);
|
||||||
|
table.getFilterCriteria().forEach((name, value) -> queryAttrs.put(name, value.get(0)));
|
||||||
|
|
||||||
|
final String downloadURL = this.downloadService
|
||||||
|
.createDownloadURL(
|
||||||
|
SEBClientLogExport.class,
|
||||||
|
fileName,
|
||||||
|
queryAttrs);
|
||||||
|
|
||||||
|
urlLauncher.openURL(downloadURL);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Failed open export log download: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
private PageAction getOpenDelete(
|
private PageAction getOpenDelete(
|
||||||
final PageAction pageAction,
|
final PageAction pageAction,
|
||||||
final MultiValueMap<String, String> filterCriteria) {
|
final MultiValueMap<String, String> filterCriteria) {
|
||||||
|
|
|
@ -770,6 +770,10 @@ public enum ActionDefinition {
|
||||||
ImageIcon.DELETE,
|
ImageIcon.DELETE,
|
||||||
PageStateDefinitionImpl.SEB_CLIENT_LOGS,
|
PageStateDefinitionImpl.SEB_CLIENT_LOGS,
|
||||||
ActionCategory.LOGS_SEB_CLIENT_LIST),
|
ActionCategory.LOGS_SEB_CLIENT_LIST),
|
||||||
|
LOGS_SEB_CLIENT_EXPORT_CSV(
|
||||||
|
new LocTextKey("sebserver.seblogs.action.export.csv"),
|
||||||
|
ImageIcon.EXPORT,
|
||||||
|
ActionCategory.LOGS_SEB_CLIENT_LIST),
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -40,17 +40,8 @@ public abstract class AbstractDownloadServiceHandler implements DownloadServiceH
|
||||||
log.debug("download requested... trying to get needed parameter from request");
|
log.debug("download requested... trying to get needed parameter from request");
|
||||||
|
|
||||||
final String modelId = request.getParameter(API.PARAM_MODEL_ID);
|
final String modelId = request.getParameter(API.PARAM_MODEL_ID);
|
||||||
if (StringUtils.isBlank(modelId)) {
|
|
||||||
log.error(
|
|
||||||
"Mandatory modelId parameter not found within HttpServletRequest. Download request is ignored");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug(
|
log.debug("Found modelId: {} for {} download.", modelId);
|
||||||
"Found modelId: {} for {} download. Trying to request webservice...",
|
|
||||||
modelId,
|
|
||||||
downloadFileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String parentModelId = request.getParameter(API.PARAM_PARENT_MODEL_ID);
|
final String parentModelId = request.getParameter(API.PARAM_PARENT_MODEL_ID);
|
||||||
|
@ -67,7 +58,7 @@ public abstract class AbstractDownloadServiceHandler implements DownloadServiceH
|
||||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, header);
|
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, header);
|
||||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
|
||||||
webserviceCall(modelId, parentModelId, response.getOutputStream());
|
webserviceCall(modelId, parentModelId, response.getOutputStream(), request);
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error(
|
log.error(
|
||||||
|
@ -76,6 +67,10 @@ public abstract class AbstractDownloadServiceHandler implements DownloadServiceH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void webserviceCall(String modelId, String parentModelId, OutputStream downloadOut);
|
protected abstract void webserviceCall(
|
||||||
|
String modelId,
|
||||||
|
String parentModelId,
|
||||||
|
OutputStream downloadOut,
|
||||||
|
HttpServletRequest request);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,14 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import java.util.Collection;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
import java.util.Map;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.rap.rwt.RWT;
|
import org.eclipse.rap.rwt.RWT;
|
||||||
import org.eclipse.rap.rwt.service.ServiceHandler;
|
import org.eclipse.rap.rwt.service.ServiceHandler;
|
||||||
|
@ -19,12 +24,10 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import java.util.Collection;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import java.util.Map;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/** Implements a eclipse RAP ServiceHandler to handle downloads */
|
/** Implements a eclipse RAP ServiceHandler to handle downloads */
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -73,6 +76,33 @@ public class DownloadService implements ServiceHandler {
|
||||||
.processDownload(request, response);
|
.processDownload(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String createDownloadURL(
|
||||||
|
final Class<? extends DownloadServiceHandler> handlerClass,
|
||||||
|
final String downloadFileName,
|
||||||
|
final Map<String, String> queryAttrs) {
|
||||||
|
|
||||||
|
final StringBuilder url = new StringBuilder()
|
||||||
|
.append(RWT.getServiceManager()
|
||||||
|
.getServiceHandlerUrl(DownloadService.DOWNLOAD_SERVICE_NAME))
|
||||||
|
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||||
|
.append(DownloadService.HANDLER_NAME_PARAMETER)
|
||||||
|
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||||
|
.append(handlerClass.getSimpleName())
|
||||||
|
.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||||
|
.append(DownloadService.DOWNLOAD_FILE_NAME)
|
||||||
|
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||||
|
.append(downloadFileName);
|
||||||
|
|
||||||
|
queryAttrs.forEach((name, value) -> {
|
||||||
|
url.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||||
|
.append(name)
|
||||||
|
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||||
|
.append(Utils.encodeFormURL_UTF_8(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public String createDownloadURL(
|
public String createDownloadURL(
|
||||||
final String modelId,
|
final String modelId,
|
||||||
final Class<? extends DownloadServiceHandler> handlerClass,
|
final Class<? extends DownloadServiceHandler> handlerClass,
|
||||||
|
|
|
@ -12,6 +12,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -35,14 +37,16 @@ public class SEBClientConfigDownload extends AbstractDownloadServiceHandler {
|
||||||
|
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
|
|
||||||
protected SEBClientConfigDownload(
|
protected SEBClientConfigDownload(final RestService restService) {
|
||||||
final RestService restService) {
|
|
||||||
|
|
||||||
this.restService = restService;
|
this.restService = restService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
protected void webserviceCall(
|
||||||
|
final String modelId,
|
||||||
|
final String parentModelId,
|
||||||
|
final OutputStream downloadOut,
|
||||||
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
final RestCall<InputStream>.RestCallBuilder restCallBuilder = this.restService
|
final RestCall<InputStream>.RestCallBuilder restCallBuilder = this.restService
|
||||||
.getBuilder(ExportClientConfig.class)
|
.getBuilder(ExportClientConfig.class)
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ExportSEBClientLogs;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class SEBClientLogExport extends AbstractDownloadServiceHandler {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SEBClientLogExport.class);
|
||||||
|
|
||||||
|
private final RestService restService;
|
||||||
|
|
||||||
|
protected SEBClientLogExport(final RestService restService) {
|
||||||
|
this.restService = restService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void webserviceCall(
|
||||||
|
final String modelId,
|
||||||
|
final String parentModelId,
|
||||||
|
final OutputStream downloadOut,
|
||||||
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
|
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
final Enumeration<String> paramNames = request.getParameterNames();
|
||||||
|
while (paramNames.hasMoreElements()) {
|
||||||
|
final String param = paramNames.nextElement();
|
||||||
|
queryParams.add(param, String.valueOf(request.getParameter(param)));
|
||||||
|
}
|
||||||
|
|
||||||
|
final InputStream input = this.restService
|
||||||
|
.getBuilder(ExportSEBClientLogs.class)
|
||||||
|
.withQueryParams(queryParams)
|
||||||
|
.call()
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
try {
|
||||||
|
IOUtils.copyLarge(input, downloadOut);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
log.error(
|
||||||
|
"Unexpected error while streaming incoming config data from web-service to output-stream of download response: ",
|
||||||
|
e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
downloadOut.flush();
|
||||||
|
downloadOut.close();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
log.error("Unexpected error while trying to close download output-stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -37,7 +39,11 @@ public class SEBExamConfigDownload extends AbstractDownloadServiceHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
protected void webserviceCall(
|
||||||
|
final String modelId,
|
||||||
|
final String parentModelId,
|
||||||
|
final OutputStream downloadOut,
|
||||||
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
final InputStream input = this.restService.getBuilder(ExportExamConfig.class)
|
final InputStream input = this.restService.getBuilder(ExportExamConfig.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||||
|
|
|
@ -12,6 +12,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -37,7 +39,11 @@ public class SEBExamConfigPlaintextDownload extends AbstractDownloadServiceHandl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
protected void webserviceCall(
|
||||||
|
final String modelId,
|
||||||
|
final String parentModelId,
|
||||||
|
final OutputStream downloadOut,
|
||||||
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
final InputStream input = this.restService.getBuilder(ExportPlainXML.class)
|
final InputStream input = this.restService.getBuilder(ExportPlainXML.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.AbstractExportCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class ExportSEBClientLogs extends AbstractExportCall {
|
||||||
|
|
||||||
|
public ExportSEBClientLogs() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.UNDEFINED,
|
||||||
|
EntityType.CLIENT_EVENT,
|
||||||
|
new TypeReference<InputStream>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.SEB_CLIENT_EVENT_ENDPOINT
|
||||||
|
+ API.SEB_CLIENT_EVENT_EXPORT_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -243,6 +243,14 @@ public class EntityTable<ROW> {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSortColumn() {
|
||||||
|
return this.sortColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageSortOrder getSortOrder() {
|
||||||
|
return this.sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
public EntityType getEntityType() {
|
public EntityType getEntityType() {
|
||||||
if (this.pageSupplier != null) {
|
if (this.pageSupplier != null) {
|
||||||
return this.pageSupplier.getEntityType();
|
return this.pageSupplier.getEntityType();
|
||||||
|
|
|
@ -94,9 +94,9 @@ public interface PaginationService {
|
||||||
* @param tableName the name of the SQL table on which the pagination is applying to
|
* @param tableName the name of the SQL table on which the pagination is applying to
|
||||||
* @param delegate a collection supplier the does the underling SQL query with specified pagination attributes
|
* @param delegate a collection supplier the does the underling SQL query with specified pagination attributes
|
||||||
* @return Result refers to a Collection of specified type of objects or to an exception on error case */
|
* @return Result refers to a Collection of specified type of objects or to an exception on error case */
|
||||||
<T> Result<Collection<T>> fetch(
|
<T> Result<Page<T>> getPageOf(
|
||||||
final int pageNumber,
|
final Integer pageNumber,
|
||||||
final int pageSize,
|
final Integer pageSize,
|
||||||
final String sort,
|
final String sort,
|
||||||
final String tableName,
|
final String tableName,
|
||||||
final Supplier<Result<Collection<T>>> delegate);
|
final Supplier<Result<Collection<T>>> delegate);
|
||||||
|
|
|
@ -148,16 +148,25 @@ public class PaginationServiceImpl implements PaginationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Result<Collection<T>> fetch(
|
public <T> Result<Page<T>> getPageOf(
|
||||||
final int pageNumber,
|
final Integer pageNumber,
|
||||||
final int pageSize,
|
final Integer pageSize,
|
||||||
final String sort,
|
final String sort,
|
||||||
final String tableName,
|
final String tableName,
|
||||||
final Supplier<Result<Collection<T>>> delegate) {
|
final Supplier<Result<Collection<T>>> delegate) {
|
||||||
|
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
setPagination(pageNumber, pageSize, sort, tableName);
|
//final SqlTable table = SqlTable.of(tableName);
|
||||||
return delegate.get().getOrThrow();
|
final com.github.pagehelper.Page<Object> page =
|
||||||
|
setPagination(pageNumber, pageSize, sort, tableName);
|
||||||
|
|
||||||
|
final Collection<T> list = delegate.get().getOrThrow();
|
||||||
|
|
||||||
|
return new Page<>(
|
||||||
|
page.getPages(),
|
||||||
|
page.getPageNum(),
|
||||||
|
sort,
|
||||||
|
list);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
|
|
||||||
public interface SEBClientEventAdminService {
|
public interface SEBClientEventAdminService {
|
||||||
|
@ -30,6 +31,7 @@ public interface SEBClientEventAdminService {
|
||||||
String sort,
|
String sort,
|
||||||
ExportType exportType,
|
ExportType exportType,
|
||||||
boolean includeConnectionDetails,
|
boolean includeConnectionDetails,
|
||||||
boolean includeExamDetails);
|
boolean includeExamDetails,
|
||||||
|
final SEBServerUser currentUser);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -30,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport.ErrorEntry;
|
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport.ErrorEntry;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
|
@ -40,7 +42,6 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecord
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientEventDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
|
@ -58,19 +59,16 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
private final ClientEventDAO clientEventDAO;
|
private final ClientEventDAO clientEventDAO;
|
||||||
private final SEBClientEventExportTransactionHandler sebClientEventExportTransactionHandler;
|
private final SEBClientEventExportTransactionHandler sebClientEventExportTransactionHandler;
|
||||||
private final EnumMap<ExportType, SEBClientEventExporter> exporter;
|
private final EnumMap<ExportType, SEBClientEventExporter> exporter;
|
||||||
private final AuthorizationService authorizationService;
|
|
||||||
|
|
||||||
public SEBClientEventAdminServiceImpl(
|
public SEBClientEventAdminServiceImpl(
|
||||||
final PaginationService paginationService,
|
final PaginationService paginationService,
|
||||||
final ClientEventDAO clientEventDAO,
|
final ClientEventDAO clientEventDAO,
|
||||||
final SEBClientEventExportTransactionHandler sebClientEventExportTransactionHandler,
|
final SEBClientEventExportTransactionHandler sebClientEventExportTransactionHandler,
|
||||||
final Collection<SEBClientEventExporter> exporter,
|
final Collection<SEBClientEventExporter> exporter) {
|
||||||
final AuthorizationService authorizationService) {
|
|
||||||
|
|
||||||
this.paginationService = paginationService;
|
this.paginationService = paginationService;
|
||||||
this.clientEventDAO = clientEventDAO;
|
this.clientEventDAO = clientEventDAO;
|
||||||
this.sebClientEventExportTransactionHandler = sebClientEventExportTransactionHandler;
|
this.sebClientEventExportTransactionHandler = sebClientEventExportTransactionHandler;
|
||||||
this.authorizationService = authorizationService;
|
|
||||||
|
|
||||||
this.exporter = new EnumMap<>(ExportType.class);
|
this.exporter = new EnumMap<>(ExportType.class);
|
||||||
exporter.forEach(exp -> this.exporter.putIfAbsent(exp.exportType(), exp));
|
exporter.forEach(exp -> this.exporter.putIfAbsent(exp.exportType(), exp));
|
||||||
|
@ -110,15 +108,32 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
final String sort,
|
final String sort,
|
||||||
final ExportType exportType,
|
final ExportType exportType,
|
||||||
final boolean includeConnectionDetails,
|
final boolean includeConnectionDetails,
|
||||||
final boolean includeExamDetails) {
|
final boolean includeExamDetails,
|
||||||
|
final SEBServerUser currentUser) {
|
||||||
|
|
||||||
new exportRunner(
|
try {
|
||||||
this.exporter.get(exportType),
|
|
||||||
includeConnectionDetails,
|
new exportRunner(
|
||||||
includeExamDetails,
|
this.exporter.get(exportType),
|
||||||
new Pager(filterMap, sort),
|
includeConnectionDetails,
|
||||||
output)
|
includeExamDetails,
|
||||||
.run();
|
new Pager(filterMap, sort),
|
||||||
|
output)
|
||||||
|
.run(currentUser);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Unexpected error during export SEB logs: ", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
output.flush();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
output.close();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,15 +161,12 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
this.pager = pager;
|
this.pager = pager;
|
||||||
this.output = output;
|
this.output = output;
|
||||||
|
|
||||||
this.connectionCache = includeConnectionDetails ? new HashMap<>() : null;
|
this.connectionCache = new HashMap<>();
|
||||||
this.examCache = includeExamDetails ? new HashMap<>() : null;
|
this.examCache = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run(final SEBServerUser currentUser) {
|
||||||
|
|
||||||
final SEBServerUser currentUser = SEBClientEventAdminServiceImpl.this.authorizationService
|
|
||||||
.getUserService()
|
|
||||||
.getCurrentUser();
|
|
||||||
final EnumSet<UserRole> userRoles = currentUser.getUserRoles();
|
final EnumSet<UserRole> userRoles = currentUser.getUserRoles();
|
||||||
final boolean isSupporterOnly = userRoles.size() == 1 && userRoles.contains(UserRole.EXAM_SUPPORTER);
|
final boolean isSupporterOnly = userRoles.size() == 1 && userRoles.contains(UserRole.EXAM_SUPPORTER);
|
||||||
|
|
||||||
|
@ -182,7 +194,7 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
if (!this.connectionCache.containsKey(connectionId)) {
|
if (!this.connectionCache.containsKey(connectionId)) {
|
||||||
SEBClientEventAdminServiceImpl.this.sebClientEventExportTransactionHandler
|
SEBClientEventAdminServiceImpl.this.sebClientEventExportTransactionHandler
|
||||||
.clientConnectionById(connectionId)
|
.clientConnectionById(connectionId)
|
||||||
.map(e -> this.connectionCache.put(connectionId, e))
|
.onSuccess(rec -> this.connectionCache.put(rec.getId(), rec))
|
||||||
.onError(error -> log.error("Failed to get ClientConnectionRecord for id: {}",
|
.onError(error -> log.error("Failed to get ClientConnectionRecord for id: {}",
|
||||||
connectionId,
|
connectionId,
|
||||||
error));
|
error));
|
||||||
|
@ -197,7 +209,7 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
if (!this.examCache.containsKey(examId)) {
|
if (!this.examCache.containsKey(examId)) {
|
||||||
SEBClientEventAdminServiceImpl.this.sebClientEventExportTransactionHandler
|
SEBClientEventAdminServiceImpl.this.sebClientEventExportTransactionHandler
|
||||||
.examById(examId)
|
.examById(examId)
|
||||||
.map(e -> this.examCache.put(examId, e))
|
.onSuccess(e -> this.examCache.put(examId, e))
|
||||||
.onError(error -> log.error("Failed to get Exam for id: {}",
|
.onError(error -> log.error("Failed to get Exam for id: {}",
|
||||||
examId,
|
examId,
|
||||||
error));
|
error));
|
||||||
|
@ -212,7 +224,7 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
private final FilterMap filterMap;
|
private final FilterMap filterMap;
|
||||||
private final String sort;
|
private final String sort;
|
||||||
|
|
||||||
private int pageNumber = 0;
|
private int pageNumber = 1;
|
||||||
private final int pageSize = 100;
|
private final int pageSize = 100;
|
||||||
|
|
||||||
private Collection<ClientEventRecord> nextRecords;
|
private Collection<ClientEventRecord> nextRecords;
|
||||||
|
@ -242,16 +254,22 @@ public class SEBClientEventAdminServiceImpl implements SEBClientEventAdminServic
|
||||||
private void fetchNext() {
|
private void fetchNext() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
this.nextRecords = SEBClientEventAdminServiceImpl.this.paginationService.fetch(
|
final Page<ClientEventRecord> nextPage = SEBClientEventAdminServiceImpl.this.paginationService
|
||||||
this.pageNumber,
|
.getPageOf(
|
||||||
this.pageSize,
|
this.pageNumber,
|
||||||
this.sort,
|
this.pageSize,
|
||||||
ClientEventRecordDynamicSqlSupport.clientEventRecord.name(),
|
this.sort,
|
||||||
() -> SEBClientEventAdminServiceImpl.this.sebClientEventExportTransactionHandler
|
ClientEventRecordDynamicSqlSupport.clientEventRecord.name(),
|
||||||
.allMatching(this.filterMap, Utils.truePredicate()))
|
() -> SEBClientEventAdminServiceImpl.this.sebClientEventExportTransactionHandler
|
||||||
|
.allMatching(this.filterMap, Utils.truePredicate()))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
this.pageNumber++;
|
if (nextPage.getPageNumber() == this.pageNumber) {
|
||||||
|
this.nextRecords = nextPage.content;
|
||||||
|
this.pageNumber++;
|
||||||
|
} else {
|
||||||
|
this.nextRecords = null;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to fetch next batch: ", e);
|
log.error("Failed to fetch next batch: ", e);
|
||||||
|
|
|
@ -13,16 +13,22 @@ import java.io.OutputStream;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.ExportType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.SEBClientEventExporter;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.SEBClientEventExporter;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@WebServiceProfile
|
||||||
public class SEBClientEventCSVExporter implements SEBClientEventExporter {
|
public class SEBClientEventCSVExporter implements SEBClientEventExporter {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SEBClientEventCSVExporter.class);
|
private static final Logger log = LoggerFactory.getLogger(SEBClientEventCSVExporter.class);
|
||||||
|
@ -55,6 +61,12 @@ public class SEBClientEventCSVExporter implements SEBClientEventExporter {
|
||||||
output.write(Utils.toByteArray(builder));
|
output.write(Utils.toByteArray(builder));
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
log.error("Failed to stream header: ", e);
|
log.error("Failed to stream header: ", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
output.flush();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,23 +81,36 @@ public class SEBClientEventCSVExporter implements SEBClientEventExporter {
|
||||||
final EventType type = EventType.byId(eventData.getType());
|
final EventType type = EventType.byId(eventData.getType());
|
||||||
|
|
||||||
builder.append(type.name());
|
builder.append(type.name());
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toCSVString(eventData.getText()));
|
builder.append(Utils.toCSVString(eventData.getText()));
|
||||||
builder.append(eventData.getNumericValue());
|
builder.append(Constants.COMMA);
|
||||||
|
builder.append(eventData.getNumericValue() != null ? eventData.getNumericValue() : "");
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toDateTimeUTC(eventData.getClientTime()));
|
builder.append(Utils.toDateTimeUTC(eventData.getClientTime()));
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toDateTimeUTC(eventData.getServerTime()));
|
builder.append(Utils.toDateTimeUTC(eventData.getServerTime()));
|
||||||
|
|
||||||
if (connectionData != null) {
|
if (connectionData != null) {
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toCSVString(connectionData.getExamUserSessionId()));
|
builder.append(Utils.toCSVString(connectionData.getExamUserSessionId()));
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toCSVString(connectionData.getClientAddress()));
|
builder.append(Utils.toCSVString(connectionData.getClientAddress()));
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(connectionData.getStatus());
|
builder.append(connectionData.getStatus());
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(connectionData.getConnectionToken());
|
builder.append(connectionData.getConnectionToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examData != null) {
|
if (examData != null) {
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toCSVString(examData.getName()));
|
builder.append(Utils.toCSVString(examData.getName()));
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(Utils.toCSVString(examData.getDescription()));
|
builder.append(Utils.toCSVString(examData.getDescription()));
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(examData.getType().name());
|
builder.append(examData.getType().name());
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(examData.getStartTime());
|
builder.append(examData.getStartTime());
|
||||||
|
builder.append(Constants.COMMA);
|
||||||
builder.append(examData.getEndTime());
|
builder.append(examData.getEndTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +120,12 @@ public class SEBClientEventCSVExporter implements SEBClientEventExporter {
|
||||||
output.write(Utils.toByteArray(builder));
|
output.write(Utils.toByteArray(builder));
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
log.error("Failed to stream header: ", e);
|
log.error("Failed to stream header: ", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
output.flush();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.io.PipedOutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -17,6 +19,7 @@ import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.mybatis.dynamic.sql.SqlTable;
|
import org.mybatis.dynamic.sql.SqlTable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -157,6 +160,14 @@ public class ClientEventController extends ReadonlyEntityController<ClientEvent,
|
||||||
required = true,
|
required = true,
|
||||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
@RequestParam(name = API.SEB_CLIENT_EVENT_EXPORT_TYPE, required = true) final ExportType type,
|
@RequestParam(name = API.SEB_CLIENT_EVENT_EXPORT_TYPE, required = true) final ExportType type,
|
||||||
|
@RequestParam(
|
||||||
|
name = API.SEB_CLIENT_EVENT_EXPORT_INCLUDE_CONNECTIONS,
|
||||||
|
required = false,
|
||||||
|
defaultValue = "true") final boolean includeConnectionDetails,
|
||||||
|
@RequestParam(
|
||||||
|
name = API.SEB_CLIENT_EVENT_EXPORT_INCLUDE_EXAMS,
|
||||||
|
required = false,
|
||||||
|
defaultValue = "false") final boolean includeExamDetails,
|
||||||
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
|
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
|
||||||
@RequestParam final MultiValueMap<String, String> allRequestParams,
|
@RequestParam final MultiValueMap<String, String> allRequestParams,
|
||||||
final HttpServletRequest request,
|
final HttpServletRequest request,
|
||||||
|
@ -169,21 +180,31 @@ public class ClientEventController extends ReadonlyEntityController<ClientEvent,
|
||||||
populateFilterMap(filterMap, institutionId, sort);
|
populateFilterMap(filterMap, institutionId, sort);
|
||||||
|
|
||||||
final ServletOutputStream outputStream = response.getOutputStream();
|
final ServletOutputStream outputStream = response.getOutputStream();
|
||||||
|
PipedOutputStream pout;
|
||||||
|
PipedInputStream pin;
|
||||||
try {
|
try {
|
||||||
|
pout = new PipedOutputStream();
|
||||||
|
pin = new PipedInputStream(pout);
|
||||||
|
|
||||||
|
final SEBServerUser currentUser = this.authorization
|
||||||
|
.getUserService()
|
||||||
|
.getCurrentUser();
|
||||||
|
|
||||||
this.sebClientEventAdminService.exportSEBClientLogs(
|
this.sebClientEventAdminService.exportSEBClientLogs(
|
||||||
outputStream,
|
pout,
|
||||||
filterMap,
|
filterMap,
|
||||||
sort,
|
sort,
|
||||||
type,
|
type,
|
||||||
false,
|
includeConnectionDetails,
|
||||||
false);
|
includeExamDetails,
|
||||||
|
currentUser);
|
||||||
|
|
||||||
|
IOUtils.copyLarge(pin, outputStream);
|
||||||
|
|
||||||
response.setStatus(HttpStatus.OK.value());
|
response.setStatus(HttpStatus.OK.value());
|
||||||
} catch (final Exception e) {
|
|
||||||
log.error("Unexpected error while trying to export SEB client logs: ", e);
|
outputStream.flush();
|
||||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
|
||||||
} finally {
|
} finally {
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
|
|
|
@ -1867,6 +1867,7 @@ sebserver.seblogs.form.column.exam.endTime=End Time
|
||||||
sebserver.seblogs.form.column.exam.endTime.tooltip=The end date and time of the exam
|
sebserver.seblogs.form.column.exam.endTime.tooltip=The end date and time of the exam
|
||||||
|
|
||||||
sebserver.seblogs.action.delete=Delete Logs
|
sebserver.seblogs.action.delete=Delete Logs
|
||||||
|
sebserver.seblogs.action.export.csv=Export CSV
|
||||||
sebserver.seblogs.delete.form.title=Delete SEB Logs
|
sebserver.seblogs.delete.form.title=Delete SEB Logs
|
||||||
sebserver.seblogs.delete.form.info=This will delete all SEB client logs from the current filtered list.<br/>Please check carefully if all SEB client logs from the list shall be deleted.<br/><br/>There are currently {0} logs within the list.
|
sebserver.seblogs.delete.form.info=This will delete all SEB client logs from the current filtered list.<br/>Please check carefully if all SEB client logs from the list shall be deleted.<br/><br/>There are currently {0} logs within the list.
|
||||||
sebserver.seblogs.delete.action.delete=Delete All Logs
|
sebserver.seblogs.delete.action.delete=Delete All Logs
|
||||||
|
|
Loading…
Reference in a new issue