From a32535e15e8c36a8823e90a373c2b8f112a1bb46 Mon Sep 17 00:00:00 2001 From: RAJVEER42 Date: Wed, 1 Jul 2026 11:33:44 +0530 Subject: [PATCH] feat(license): wrap GET /license/export-csv endpoint Refs #52. Adds LicenseEndpoint.export_licenses_csv() for GET /license/export-csv, the export counterpart to the merged import_licenses_csv() (#185). Returns the exported licenses as CSV text and accepts an optional license id (0 = all). Verified against Fossology 4.4.0 (API 1.6.1) running in a container: an export->import round-trip test passes (idempotent), plus a mocked 403 error path. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: RAJVEER42 --- fossology/license.py | 22 ++++++++++++++++++++++ tests/test_license.py | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/fossology/license.py b/fossology/license.py index 0f5b5c1..a9a5758 100644 --- a/fossology/license.py +++ b/fossology/license.py @@ -195,6 +195,28 @@ def import_licenses_csv( description = f"Unable to import licenses from {csv_file}" raise FossologyApiError(description, response) + def export_licenses_csv(self, license_id: int = 0) -> str: + """Export licenses as CSV. + + API Endpoint: GET /license/export-csv + + :param license_id: id of the license to export, 0 to export all (default: 0) + :type license_id: int + :return: the exported licenses as CSV text + :rtype: str + :raises FossologyApiError: if the REST call failed + """ + response = self.session.get( + f"{self.api}/license/export-csv", params={"id": license_id} + ) + + if response.status_code == 200: + logger.info(f"Exported licenses as CSV (id={license_id})") + return response.text + + description = f"Unable to export licenses as CSV (id={license_id})" + raise FossologyApiError(description, response) + def update_license( self, shortname: str, diff --git a/tests/test_license.py b/tests/test_license.py index 7e1c49a..07b74b3 100644 --- a/tests/test_license.py +++ b/tests/test_license.py @@ -86,6 +86,28 @@ def test_import_licenses_csv_request_payload( assert "shortname,fullname\nFoo,Foo License" in body +def test_export_import_licenses_csv_roundtrip(foss: fossology.Fossology, tmp_path): + # Export the full license set, then re-import it. The export format matches + # the import format, and re-importing existing licenses is idempotent. + exported = foss.export_licenses_csv() + assert isinstance(exported, str) + assert exported + csv_path = tmp_path / "exported.csv" + csv_path.write_text(exported) + message = foss.import_licenses_csv(str(csv_path)) + assert "Read csv" in message + + +@responses.activate +def test_export_licenses_csv_error(foss_server: str, foss: fossology.Fossology): + responses.add( + responses.GET, f"{foss_server}/api/v1/license/export-csv", status=403 + ) + with pytest.raises(FossologyApiError) as excinfo: + foss.export_licenses_csv() + assert "Unable to export licenses as CSV (id=0)" in str(excinfo.value) + + @responses.activate def test_detail_license_error(foss_server: str, foss: fossology.Fossology): responses.add(responses.GET, f"{foss_server}/api/v1/license/Blah", status=500)