/* * Copyright 2015-2017 Hewlett-Packard Enterprise Development Company, L.P. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. */ package com.hp.autonomy.frontend.find.core.export; import com.fasterxml.jackson.databind.ObjectMapper; import com.hp.autonomy.frontend.find.core.export.service.ExportFormat; import com.hp.autonomy.frontend.find.core.export.service.ExportServiceFactory; import com.hp.autonomy.frontend.find.core.export.service.VisualDataExportService; import com.hp.autonomy.frontend.find.core.web.ControllerUtils; import com.hp.autonomy.frontend.find.core.web.ErrorModelAndViewInfo; import com.hp.autonomy.frontend.find.core.web.RequestMapper; import com.hp.autonomy.frontend.reports.powerpoint.TemplateLoadException; import com.hp.autonomy.frontend.reports.powerpoint.dto.DategraphData; import com.hp.autonomy.frontend.reports.powerpoint.dto.ListData; import com.hp.autonomy.frontend.reports.powerpoint.dto.MapData; import com.hp.autonomy.frontend.reports.powerpoint.dto.ReportData; import com.hp.autonomy.frontend.reports.powerpoint.dto.SunburstData; import com.hp.autonomy.frontend.reports.powerpoint.dto.TableData; import com.hp.autonomy.frontend.reports.powerpoint.dto.TopicMapData; import com.hp.autonomy.searchcomponents.core.search.QueryRequest; import org.apache.commons.io.FilenameUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Collection; public abstract class ExportController<R extends QueryRequest<?>, E extends Exception> { public static final String EXPORT_PATH = "/api/bi/export"; public static final String PPTX_PATH = "/pptx"; protected static final String CSV_PATH = "/csv"; protected static final String TOPIC_MAP_PATH = "/topic-map"; protected static final String SUNBURST_PATH = "/sunburst"; protected static final String TABLE_PATH = "/table"; protected static final String MAP_PATH = "/map"; protected static final String LIST_PATH = "/list"; protected static final String DATE_GRAPH_PATH = "/date-graph"; protected static final String REPORT_PATH = "/report"; protected static final String SELECTED_EXPORT_FIELDS_PARAM = "selectedFieldIds"; protected static final String QUERY_REQUEST_PARAM = "queryRequest"; protected static final String DATA_PARAM = "data"; protected static final String TITLE_PARAM = "data"; protected static final String RESULTS_PARAM = "results"; protected static final String SORT_BY_PARAM = "sortBy"; protected static final String MULTI_PAGE_PARAM = "multiPage"; protected static final String MULTI_PAGE_PARAM_DEFAULT = "false"; private static final String QUERY_RESULT_EXPORT_FILE_NAME = "query-results"; private static final String TOPIC_MAP_EXPORT_FILE_NAME = "topic-map"; private static final String SUNBURST_EXPORT_FILE_NAME = "sunburst"; private static final String TABLE_EXPORT_FILE_NAME = "table"; private static final String MAP_EXPORT_FILE_NAME = "map"; private static final String LIST_EXPORT_FILE_NAME = "list"; private static final String DATE_GRAPH_EXPORT_FILE_NAME = "date-graph"; private static final String REPORT_EXPORT_FILE_NAME = "report"; protected final ExportServiceFactory<R, E> exportServiceFactory; private final RequestMapper<R> requestMapper; private final ControllerUtils controllerUtils; private final ObjectMapper objectMapper = new ObjectMapper(); protected ExportController(final RequestMapper<R> requestMapper, final ControllerUtils controllerUtils, final ExportServiceFactory<R, E> exportServiceFactory) { this.requestMapper = requestMapper; this.controllerUtils = controllerUtils; this.exportServiceFactory = exportServiceFactory; } protected abstract ExportFormat getExportFormat(); @ResponseBody public ResponseEntity<byte[]> exportQueryResults( @RequestParam(QUERY_REQUEST_PARAM) final String queryRequestJSON, // required = false to prevent Spring errors if the user asks for a CSV with no fields marked for exportQueryResults. // The UI should not allow the User to send a request for a CSV with nothing in it. @RequestParam(value = SELECTED_EXPORT_FIELDS_PARAM, required = false) final Collection<String> selectedFieldNames ) throws IOException, E { final R queryRequest = requestMapper.parseQueryRequest(queryRequestJSON); return writeDataToOutputStream(outputStream -> export(outputStream, queryRequest, selectedFieldNames), QUERY_RESULT_EXPORT_FILE_NAME); } protected abstract void export(final OutputStream outputStream, final R queryRequest, final Collection<String> selectedFieldNames) throws E, IOException; //TODO improve to inform what went wrong with exportQueryResults, rather than generic just error 500. @ExceptionHandler @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ModelAndView handleException( final Exception e, final HttpServletRequest request, final ServletResponse response ) { response.reset(); return controllerUtils.buildErrorModelAndView( new ErrorModelAndViewInfo.Builder() .setRequest(request) .setMainMessageCode("error.internalServerErrorMain") .setSubMessageCode("error.internalServerErrorSub") .setSubMessageArguments(null) .setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value()) .setContactSupport(true) .setException(e) .build() ); } public ResponseEntity<byte[]> topicMap( @RequestParam(DATA_PARAM) final String rawData ) throws IOException, TemplateLoadException, E { final TopicMapData data = objectMapper.readValue(rawData, TopicMapData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().topicMap(outputStream, data), TOPIC_MAP_EXPORT_FILE_NAME); } public ResponseEntity<byte[]> sunburst( @RequestParam(DATA_PARAM) final String rawData ) throws IOException, TemplateLoadException, E { final SunburstData data = objectMapper.readValue(rawData, SunburstData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().sunburst(outputStream, data), SUNBURST_EXPORT_FILE_NAME); } public ResponseEntity<byte[]> table( @RequestParam(DATA_PARAM) final String rawData, @RequestParam(TITLE_PARAM) final String title ) throws IOException, TemplateLoadException, E { final TableData data = objectMapper.readValue(rawData, TableData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().table(outputStream, data, title), TABLE_EXPORT_FILE_NAME); } public ResponseEntity<byte[]> map( @RequestParam(DATA_PARAM) final String rawData, @RequestParam(TITLE_PARAM) final String title ) throws IOException, TemplateLoadException, E { final MapData data = objectMapper.readValue(rawData, MapData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().map(outputStream, data, title), MAP_EXPORT_FILE_NAME); } public ResponseEntity<byte[]> list( @RequestParam(DATA_PARAM) final String rawData, @RequestParam(RESULTS_PARAM) final String results, @RequestParam(SORT_BY_PARAM) final String sortBy ) throws IOException, TemplateLoadException, E { final ListData data = objectMapper.readValue(rawData, ListData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().list(outputStream, data, results, sortBy), LIST_EXPORT_FILE_NAME); } public ResponseEntity<byte[]> dateGraph( @RequestParam(DATA_PARAM) final String rawData ) throws IOException, TemplateLoadException, E { final DategraphData data = objectMapper.readValue(rawData, DategraphData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().dateGraph(outputStream, data), DATE_GRAPH_EXPORT_FILE_NAME); } public ResponseEntity<byte[]> report( @RequestParam(DATA_PARAM) final String rawData, @RequestParam(value = MULTI_PAGE_PARAM, defaultValue = "false") final boolean multiPage ) throws IOException, TemplateLoadException, E { final ReportData data = objectMapper.readValue(rawData, ReportData.class); return writeDataToOutputStream(outputStream -> getVisualDataExportService().report(outputStream, data, multiPage), REPORT_EXPORT_FILE_NAME); } private VisualDataExportService getVisualDataExportService() { return exportServiceFactory.getVisualDataExportService(getExportFormat()).orElseThrow(() -> new UnsupportedOperationException("Visual data export operation not supported for format: " + getExportFormat().name())); } private ResponseEntity<byte[]> writeDataToOutputStream(final Operation<E> operation, final String fileNameWithoutExtension) throws IOException, E { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); operation.accept(outputStream); final byte[] output = outputStream.toByteArray(); final HttpHeaders headers = new HttpHeaders(); final ExportFormat exportFormat = getExportFormat(); headers.setContentType(MediaType.parseMediaType(exportFormat.getMimeType())); final String fileName = fileNameWithoutExtension + FilenameUtils.EXTENSION_SEPARATOR + exportFormat.getExtension(); headers.setContentDispositionFormData(fileName, fileName); return new ResponseEntity<>(output, headers, HttpStatus.OK); } @FunctionalInterface private interface Operation<E extends Exception> { void accept(OutputStream outputStream) throws IOException, E; } }