diff --git a/openbas-api/src/main/java/io/openbas/rest/mapper/MapperApi.java b/openbas-api/src/main/java/io/openbas/rest/mapper/MapperApi.java index 657f63a1a0..50839e1758 100644 --- a/openbas-api/src/main/java/io/openbas/rest/mapper/MapperApi.java +++ b/openbas-api/src/main/java/io/openbas/rest/mapper/MapperApi.java @@ -18,6 +18,7 @@ import io.openbas.service.MapperService; import io.openbas.utils.pagination.SearchPaginationInput; import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; import jakarta.transaction.Transactional; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; @@ -30,6 +31,7 @@ import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.data.domain.Page; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; @@ -37,6 +39,7 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.reactive.function.UnsupportedMediaTypeException; +import javax.print.attribute.standard.Media; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -48,6 +51,7 @@ import java.util.Date; import java.util.List; import java.util.UUID; +import java.util.zip.ZipOutputStream; import static io.openbas.database.model.User.ROLE_ADMIN; import static io.openbas.database.model.User.ROLE_USER; @@ -90,21 +94,21 @@ public ImportMapper createImportMapper(@RequestBody @Valid final ImportMapperAdd } @Secured(ROLE_ADMIN) - @PostMapping(value="/api/mappers/export", - produces=MediaType.APPLICATION_OCTET_STREAM_VALUE) - public ResponseEntity exportMappers(@RequestBody @Valid final ExportMapperInput exportMapperInput) { + @PostMapping(value="/api/mappers/export") + public void exportMappers(@RequestBody @Valid final ExportMapperInput exportMapperInput, + HttpServletResponse response) { List mappers = mapperService.exportMappers(exportMapperInput.getIdsToExport()); try { String rightNow = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()); - Path filename = Path.of(System.getProperty("java.io.tmpdir"), MessageFormat.format("mappers_{0}.json", rightNow)); - FileOutputStream fos = new FileOutputStream(filename.toString(), false); - fos.write(mapper.writeValueAsString(mappers).getBytes(StandardCharsets.UTF_8)); - fos.flush(); - fos.close(); - - return ResponseEntity.ok() - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .body(new FileInputStream(filename.toString()).readAllBytes()); + String filename = MessageFormat.format("mappers_{0}.json", rightNow); + + response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); + response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + response.setStatus(HttpServletResponse.SC_OK); + + response.getOutputStream().write(mapper.writeValueAsString(mappers).getBytes(StandardCharsets.UTF_8)); + response.getOutputStream().flush(); + response.getOutputStream().close(); } catch (IOException e) { throw new RuntimeException("Error during export", e); } diff --git a/openbas-front/src/actions/mapper/mapper-actions.ts b/openbas-front/src/actions/mapper/mapper-actions.ts index ddc376740a..63e23752da 100644 --- a/openbas-front/src/actions/mapper/mapper-actions.ts +++ b/openbas-front/src/actions/mapper/mapper-actions.ts @@ -1,5 +1,13 @@ -import type { ImportMapperAddInput, ImportMapperUpdateInput, InjectsImportTestInput, RawPaginationImportMapper, SearchPaginationInput } from '../../utils/api-types'; -import { simpleCall, simpleDelCall, simplePostCall, simplePutCall } from '../../utils/Action'; +import { + ExportMapperInput, + ImportMapperAddInput, + ImportMapperUpdateInput, + InjectsImportTestInput, + RawPaginationImportMapper, + SearchPaginationInput +} from '../../utils/api-types'; +import {postReferential, simpleCall, simpleDelCall, simplePostCall, simplePutCall} from '../../utils/Action'; +import {Dispatch} from "redux"; const XLS_MAPPER_URI = '/api/mappers'; @@ -39,3 +47,13 @@ export const testXlsFile = (importId: string, input: InjectsImportTestInput) => const uri = `${XLS_MAPPER_URI}/store/${importId}`; return simplePostCall(uri, input); }; + +export const exportMapper = (input: ExportMapperInput) => { + const uri = `${XLS_MAPPER_URI}/export`; + return simplePostCall(uri, input); +}; + +export const importMapper = (formData: FormData) => (dispatch: Dispatch) => { + const uri = `${XLS_MAPPER_URI}/import`; + return postReferential(null, uri, formData)(dispatch); +}; diff --git a/openbas-front/src/admin/components/settings/data_ingestion/ImportUploaderMapper.tsx b/openbas-front/src/admin/components/settings/data_ingestion/ImportUploaderMapper.tsx new file mode 100644 index 0000000000..f3dc6fd4ff --- /dev/null +++ b/openbas-front/src/admin/components/settings/data_ingestion/ImportUploaderMapper.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import {useAppDispatch} from '../../../../utils/hooks'; +import ImportUploader from '../../../../components/common/ImportUploader'; +import {importMapper} from '../../../../actions/mapper/mapper-actions'; + +const ImportUploaderMapper = () => { + // Standard hooks + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + + const handleUpload = async (formData: FormData) => { + await dispatch(importMapper(formData)).then((result: { [x: string]: string; }) => { + if (!Object.prototype.hasOwnProperty.call(result, 'FINAL_FORM/form-error')) { + navigate(0); + } + }); + }; + + return ( + + ); +}; + +export default ImportUploaderMapper; diff --git a/openbas-front/src/admin/components/settings/data_ingestion/XlsMapperPopover.tsx b/openbas-front/src/admin/components/settings/data_ingestion/XlsMapperPopover.tsx index b9414dba5f..4a56d4b747 100644 --- a/openbas-front/src/admin/components/settings/data_ingestion/XlsMapperPopover.tsx +++ b/openbas-front/src/admin/components/settings/data_ingestion/XlsMapperPopover.tsx @@ -1,23 +1,27 @@ import React, { FunctionComponent, useState } from 'react'; import { PopoverEntry } from '../../../../components/common/ButtonPopover'; import IconPopover from '../../../../components/common/IconPopover'; -import type { RawPaginationImportMapper } from '../../../../utils/api-types'; -import { deleteMapper } from '../../../../actions/mapper/mapper-actions'; +import type {ImportMapperAddInput, RawPaginationImportMapper} from '../../../../utils/api-types'; +import {deleteMapper, exportMapper} from '../../../../actions/mapper/mapper-actions'; import DialogDelete from '../../../../components/common/DialogDelete'; import { useFormatter } from '../../../../components/i18n'; import Drawer from '../../../../components/common/Drawer'; import XlsMapperUpdate from './xls_mapper/XlsMapperUpdate'; +import {download} from '../../../../utils/utils'; +import {AxiosResponse} from 'axios'; interface Props { mapper: RawPaginationImportMapper; onUpdate?: (result: RawPaginationImportMapper) => void; onDelete?: (result: string) => void; + onExport?: (result: string) => void; } const XlsMapperPopover: FunctionComponent = ({ mapper, onUpdate, onDelete, + onExport, }) => { // Standard hooks const { t } = useFormatter(); @@ -41,9 +45,25 @@ const XlsMapperPopover: FunctionComponent = ({ handleCloseDelete(); }; + const exportMapperAction = () => { + exportMapper({ + ids_to_export: [mapper.import_mapper_id] + }).then( + (result: AxiosResponse<{ data: string }>) => { + let filename = result.headers['content-disposition'].split('filename=')[1]; + console.log(filename); + download(JSON.stringify(result.data, null, 2), filename, 'application/json'); + }, + ); + if (onExport) { + onExport(mapper.import_mapper_id); + } + }; + const entries: PopoverEntry[] = [ { label: 'Update', action: handleOpenEdit }, { label: 'Delete', action: handleOpenDelete }, + { label: 'Export', action: exportMapperAction}, ]; return ( diff --git a/openbas-front/src/admin/components/settings/data_ingestion/XlsMappers.tsx b/openbas-front/src/admin/components/settings/data_ingestion/XlsMappers.tsx index f18e2dfe20..fe2f2629f9 100644 --- a/openbas-front/src/admin/components/settings/data_ingestion/XlsMappers.tsx +++ b/openbas-front/src/admin/components/settings/data_ingestion/XlsMappers.tsx @@ -13,6 +13,7 @@ import DataIngestionMenu from '../DataIngestionMenu'; import XlsMapperCreation from './xls_mapper/XlsMapperCreation'; import PaginationComponent from '../../../../components/common/pagination/PaginationComponent'; import XlsMapperPopover from './XlsMapperPopover'; +import ImportUploaderMapper from './ImportUploaderMapper'; const useStyles = makeStyles(() => ({ container: { @@ -76,7 +77,9 @@ const XlsMappers = () => { fetch={searchMappers} searchPaginationInput={searchPaginationInput} setContent={setMappers} - /> + > + +