From 04ca22d7de6b70654b85315ce408fb71acc63a8f Mon Sep 17 00:00:00 2001 From: Marion Deveaud Date: Thu, 15 Oct 2020 15:06:46 +0200 Subject: [PATCH] refactor(0.2.0): get report name from HTTP header Interface change for `download_report`: - only the report id is accepted as parameter - removed `as_zip` option, Fossology only returns uncompressed reports - return a tuple of the report content and the file name as returned by the HTTP GET reponse header - use typehints and fix exceptions descriptions --- docs-source/conf.py | 4 ++-- fossology/report.py | 31 ++++++++++++++++--------------- pyproject.toml | 2 +- tests/test_report.py | 33 +++++++-------------------------- 4 files changed, 26 insertions(+), 44 deletions(-) diff --git a/docs-source/conf.py b/docs-source/conf.py index 6242175..e8c6c26 100644 --- a/docs-source/conf.py +++ b/docs-source/conf.py @@ -22,8 +22,8 @@ project = "fossology" copyright = "2020, Siemens AG" -# The full version, including alpha/beta/rc tags -release = "0.1.4" +# The full version, including major/minor/patch tags +release = "0.2.0" # -- General configuration --------------------------------------------------- diff --git a/fossology/report.py b/fossology/report.py index fdaeffd..ed35141 100644 --- a/fossology/report.py +++ b/fossology/report.py @@ -4,8 +4,10 @@ import re import time import logging +from typing import Tuple from tenacity import retry, TryAgain, stop_after_attempt, retry_if_exception_type +from .obj import ReportFormat, Upload from .exceptions import FossologyApiError logger = logging.getLogger(__name__) @@ -16,7 +18,7 @@ class Report: """Class dedicated to all "report" related endpoints""" @retry(retry=retry_if_exception_type(TryAgain), stop=stop_after_attempt(3)) - def generate_report(self, upload, report_format=None): + def generate_report(self, upload: Upload, report_format: ReportFormat = None): """Generate a report for a given upload API Endpoint: GET /report @@ -45,11 +47,11 @@ def generate_report(self, upload, report_format=None): time.sleep(int(wait_time)) raise TryAgain else: - description = f"Report generation for upload {upload.name} failed" + description = f"Report generation for upload {upload.uploadname} failed" raise FossologyApiError(description, response) @retry(retry=retry_if_exception_type(TryAgain), stop=stop_after_attempt(3)) - def download_report(self, report_id, as_zip=False): + def download_report(self, report_id: int) -> Tuple[str, str]: """Download a report API Endpoint: GET /report/{id} @@ -62,29 +64,28 @@ def download_report(self, report_id, as_zip=False): >>> >>> # Generate a report for upload 1 >>> report_id = foss.generate_report(foss.detail_upload(1)) - >>> report_content = foss.download_report(report_id, as_zip=True) - >>> with open(filename, "w+") as report_file: + >>> report_content, report_name = foss.download_report(report_id) + >>> with open(report_name, "w+") as report_file: >>> report_file.write(report_content) :param report_id: the id of the generated report - :param as_zip: control if the report should be generated as ZIP file (default False) :type report_id: int - :type as_zip: boolean + :return: the report content and the report name + :rtype: Tuple[str, str] :raises FossologyApiError: if the REST call failed + :raises TryAgain: if the report generation timed out after 3 retries """ - if as_zip: - headers = {"Accept": "application/zip"} - else: - headers = {"Accept": "text/plain"} - - response = self.session.get(f"{self.api}/report/{report_id}", headers=headers) + response = self.session.get(f"{self.api}/report/{report_id}") if response.status_code == 200: - return response.text + content = response.headers["Content-Disposition"] + report_name_pattern = '(^attachment; filename=")(.*)("$)' + report_name = re.match(report_name_pattern, content).group(2) + return response.text, report_name elif response.status_code == 503: wait_time = response.headers["Retry-After"] logger.debug(f"Retry get report after {wait_time} seconds") time.sleep(int(wait_time)) raise TryAgain else: - description = "Download of report {report_id} failed" + description = f"Download of report {report_id} failed" raise FossologyApiError(description, response) diff --git a/pyproject.toml b/pyproject.toml index c1870ff..af3ec03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "fossology" -version = "0.1.4" +version = "0.2.0" description = "A library to automate Fossology from Python scripts" authors = ["Marion Deveaud "] license = "MIT License" diff --git a/tests/test_report.py b/tests/test_report.py index bcc894e..2814e7a 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -7,7 +7,7 @@ from pathlib import Path from test_base import foss, logger -from test_uploads import get_upload, do_upload, upload_filename +from test_uploads import get_upload, do_upload from fossology.exceptions import FossologyApiError from fossology.obj import ReportFormat @@ -28,39 +28,20 @@ def test_generate_report(self): try: # Plain text - report = foss.download_report(report_id) - report_path = Path.cwd() / "tests/files" - report_name = upload_filename + ".spdx-report.rdf" - with open(report_path / report_name, "w+") as report_file: + report, name = foss.download_report(report_id) + report_path = Path.cwd() / "tests/files" / name + with open(report_path, "w+") as report_file: report_file.write(report) - filetype = mimetypes.guess_type(report_path / report_name) - report_stat = os.stat(report_path / report_name) + filetype = mimetypes.guess_type(report_path) + report_stat = os.stat(report_path) self.assertGreater(report_stat.st_size, 0, "Downloaded report is empty") self.assertIn( filetype[0], ("application/rdf+xml", "application/xml"), "Downloaded report is not a RDF/XML file", ) - Path(report_path / report_name).unlink() - except FossologyApiError as error: - logger.error(error.message) - - try: - # Zip - report = foss.download_report(report_id, as_zip=True) - report_path = Path.cwd() / "tests/files" - report_name = upload_filename + ".spdx-report.rdf.zip" - with open(report_path / report_name, "w+") as report_file: - report_file.write(report) - - filetype = mimetypes.guess_type(report_path / report_name) - report_stat = os.stat(report_path / report_name) - self.assertGreater(report_stat.st_size, 0, "Downloaded report is empty") - self.assertEqual( - filetype[0], "application/zip", "Downloaded report is not a ZIP file" - ) - Path(report_path / report_name).unlink() + Path(report_path).unlink() except FossologyApiError as error: logger.error(error.message)