Skip to content

Commit

Permalink
[frontend/backend] Adding import/export of XLS mappers
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimfacion committed Jul 29, 2024
1 parent 7d8a904 commit 9643cb8
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 17 deletions.
28 changes: 16 additions & 12 deletions openbas-api/src/main/java/io/openbas/rest/mapper/MapperApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,13 +31,15 @@
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;
import org.springframework.web.bind.annotation.*;
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<byte[]> exportMappers(@RequestBody @Valid final ExportMapperInput exportMapperInput) {
@PostMapping(value="/api/mappers/export")
public void exportMappers(@RequestBody @Valid final ExportMapperInput exportMapperInput,
HttpServletResponse response) {
List<ImportMapperAddInput> 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);
}
Expand Down
22 changes: 20 additions & 2 deletions openbas-front/src/actions/mapper/mapper-actions.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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);
};
Original file line number Diff line number Diff line change
@@ -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 (
<ImportUploader
title={'Import a mapper'}
handleUpload={handleUpload}
/>
);
};

export default ImportUploaderMapper;
Original file line number Diff line number Diff line change
@@ -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<Props> = ({
mapper,
onUpdate,
onDelete,
onExport,
}) => {
// Standard hooks
const { t } = useFormatter();
Expand All @@ -41,9 +45,25 @@ const XlsMapperPopover: FunctionComponent<Props> = ({
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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -76,7 +77,9 @@ const XlsMappers = () => {
fetch={searchMappers}
searchPaginationInput={searchPaginationInput}
setContent={setMappers}
/>
>
<ImportUploaderMapper/>
</PaginationComponent>
<List>
<ListItem
classes={{ root: classes.itemHead }}
Expand Down
4 changes: 4 additions & 0 deletions openbas-front/src/utils/api-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,10 @@ export interface ExpectationUpdateInput {
source_type: string;
}

export interface ExportMapperInput {
ids_to_export: string[];
}

export interface Filter {
key: string;
mode?: 'and' | 'or';
Expand Down

0 comments on commit 9643cb8

Please sign in to comment.