diff --git a/src/Routes.jsx b/src/Routes.jsx index 2705f4105..dfd526fcf 100644 --- a/src/Routes.jsx +++ b/src/Routes.jsx @@ -95,6 +95,7 @@ const DeleteUser = React.lazy(() => import("pages/Admin/Users/Delete")); const AddUser = React.lazy(() => import("pages/Admin/Users/Add")); const EditUser = React.lazy(() => import("pages/Admin/Users/Edit")); const AddLicense = React.lazy(() => import("pages/Admin/License/Create")); +const ImportLicense = React.lazy(() => import("pages/Admin/License/Import")); const SelectLicense = React.lazy(() => import("pages/Admin/License/SelectLicense") ); @@ -302,6 +303,11 @@ const Routes = () => { path={routes.admin.license.selectLicense} component={SelectLicense} /> + { + const url = endpoints.license.importCsv(); + const formdata = new FormData(); + + if (fileInput) { + formdata.append("file_input", fileInput); + formdata.append("delimiter", delimiter); + formdata.append("enclosure", enclosure); + } + return sendRequest({ + url, + method: "POST", + isMultipart: true, + headers: { + Authorization: getToken(), + }, + body: formdata, + }); +}; diff --git a/src/api/licenses.test.js b/src/api/licenses.test.js index 9e8b68738..0b0fecaa6 100644 --- a/src/api/licenses.test.js +++ b/src/api/licenses.test.js @@ -16,7 +16,11 @@ import sendRequest from "api/sendRequest"; import endpoints from "constants/endpoints"; import { getToken } from "shared/authHelper"; -import { createCandidateLicenseApi, getAllLicenseApi } from "api/licenses"; +import { + createCandidateLicenseApi, + getAllLicenseApi, + importLicenseCsvApi, +} from "api/licenses"; jest.mock("api/sendRequest"); @@ -54,6 +58,7 @@ describe("licenses", () => { const licenseUrl = "licenseUrl"; const mergeRequest = "mergeRequest"; const url = endpoints.admin.license.createCandidateLicense(); + sendRequest.mockImplementation(() => true); expect( createCandidateLicenseApi({ @@ -85,3 +90,32 @@ describe("licenses", () => { ); }); }); + +test("importLicenseCsvApi", () => { + const url = endpoints.license.importCsv(); + const expectedBody = new FormData(); + const delimiter = "shortName"; + const enclosure = "fullName"; + const fileInput = "fileInput"; + + expectedBody.append("file_input", fileInput); + expectedBody.append("delimiter", delimiter); + expectedBody.append("enclosure", enclosure); + + sendRequest.mockImplementation(() => true); + + expect(importLicenseCsvApi(fileInput, delimiter, enclosure)).toBe( + sendRequest({}) + ); + expect(sendRequest).toHaveBeenCalledWith( + expect.objectContaining({ + url, + method: "POST", + isMultipart: true, + headers: { + Authorization: getToken(), + }, + body: expectedBody, + }) + ); +}); diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx index bc6b0f47c..0023c90aa 100644 --- a/src/components/Header/index.jsx +++ b/src/components/Header/index.jsx @@ -312,6 +312,12 @@ const Header = () => { > CSV Export + + CSV Import + `${apiUrl}/license`, createCandidateLicense: () => `${apiUrl}/license`, + importCsv: () => `${apiUrl}/license/import-csv`, }, info: { info: () => `${apiUrl}/info`, diff --git a/src/constants/routes.js b/src/constants/routes.js index 832858e97..1d8649eee 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -77,6 +77,7 @@ const routes = { create: "/admin/license/create", selectLicense: "/admin/selectLicense", licenseCSV: "/licenseCSV/fossology-license-export.csv", + import: "/admin/license/import", }, mantainance: "/admin/mantainance", }, diff --git a/src/pages/Admin/License/Import/index.jsx b/src/pages/Admin/License/Import/index.jsx new file mode 100644 index 000000000..ca310e171 --- /dev/null +++ b/src/pages/Admin/License/Import/index.jsx @@ -0,0 +1,149 @@ +/* + Copyright (C) 2022 Samuel Dushimimana (dushsam100@gmail.com) + + SPDX-License-Identifier: GPL-2.0 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import React, { useState } from "react"; + +// Title +import Title from "components/Title"; +import { initialImportCsvLicense, initialMessage } from "constants/constants"; +import { Alert, Button, InputContainer, Spinner } from "components/Widgets"; +import { importLicenseCsv } from "services/licenses"; + +const ImportLicense = () => { + // Data required for importing the csv file + const [uploadFileData, setUploadFileData] = useState(initialImportCsvLicense); + // State Variables for handling Error Boundaries + const [loading, setLoading] = useState(false); + const [showMessage, setShowMessage] = useState(false); + const [message, setMessage] = useState(initialMessage); + + const handleChange = (e) => { + if (e.target.type === "fileInput") { + setUploadFileData({ + ...uploadFileData, + [e.target.name]: e.target.files[0], + }); + } else { + setUploadFileData({ + ...uploadFileData, + [e.target.name]: e.target.value, + }); + } + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + setLoading(true); + const { data } = await importLicenseCsv(uploadFileData); + setMessage({ + type: "success", + text: data.message, + }); + setUploadFileData(initialImportCsvLicense); + } catch (error) { + setMessage({ + type: "danger", + text: error.message, + }); + } finally { + setLoading(false); + setShowMessage(true); + } + }; + return ( + <> + + <div className="main-container my-3"> + <div className="row"> + <div className="col-lg-8 col-md-12 col-sm-12 col-12"> + {showMessage && ( + <Alert + type={message.type} + setShow={setShowMessage} + message={message.text} + /> + )} + <h1 className="font-size-main-heading"> + Admin Obligation CSV Import + </h1> + </div> + <div className="col-lg-8 col-md-12 col-sm-12 col-12"> + <p> + This option permits uploading a CSV file from your computer to + Your FOSSology server has imposed a maximum upload file size of + 700Mbytes. + </p> + </div> + <div className="col-lg-8 col-md-12 col-sm-12 col-12"> + <span>Select the CSV-file to upload:</span> + <InputContainer + type="file" + name="fileInput" + id="upload-file-input" + onChange={(e) => handleChange(e)} + /> + </div> + <div className="col-lg-8 col-md-12 col-sm-12 col-12"> + <div> + <InputContainer + type="text" + name="name" + id="delimiter" + onChange={handleChange} + value={uploadFileData.delimiter} + > + Delimiter: + </InputContainer> + </div> + + <div> + <InputContainer + type="text" + name="name" + id="admin-group-add-name" + onChange={handleChange} + placeholder="Group name" + value={uploadFileData.enclosure} + > + Enclosure: + </InputContainer> + </div> + </div> + <div className="col-lg-8 col-md-12 col-sm-12 col-12"> + <Button type="submit" onClick={handleSubmit} className="mt-4"> + {loading ? ( + <Spinner + as="span" + animation="border" + size="sm" + role="status" + aria-hidden="true" + /> + ) : ( + "Add" + )} + </Button> + </div> + </div> + </div> + </> + ); +}; + +export default ImportLicense; diff --git a/src/services/licenses.js b/src/services/licenses.js index 23c5f96b0..ed677106a 100644 --- a/src/services/licenses.js +++ b/src/services/licenses.js @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 Shruti Agarwal (mail2shruti.ag@gmail.com), Aman Dwivedi (aman.dwivedi5@gmail.com) + Copyright (C) 2021 Shruti Agarwal (mail2shruti.ag@gmail.com), Aman Dwivedi (aman.dwivedi5@gmail.com) , Samuel Dushimimana (dushsam100@gmail.com) SPDX-License-Identifier: GPL-2.0 @@ -16,7 +16,11 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { getAllLicenseApi, createCandidateLicenseApi } from "api/licenses"; +import { + getAllLicenseApi, + createCandidateLicenseApi, + importLicenseCsvApi, +} from "api/licenses"; // Fetching the licenses with their kind i.e (candidate, main, all) export const getAllLicense = (licenseData) => { @@ -30,3 +34,9 @@ export const createCandidateLicense = (licenseData) => { return res; }); }; + +export const importLicenseCsv = ({ fileInput, delimiter, enclosure }) => { + return importLicenseCsvApi(fileInput, delimiter, enclosure).then((res) => { + return res; + }); +};