Skip to content

Commit 86ba9bd

Browse files
authored
Merge pull request #97 from fossology/fix/content-disposition
- Add support for API version 1.4.0: adapt PUT and PATCH /upload - Add possibility to specify `wait_time` for reports download - Fix content disposition regex when downloading reports - Create new minor version 1.5.0
2 parents 56f639c + 976eabe commit 86ba9bd

22 files changed

+675
-443
lines changed

.github/workflows/fossologytests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353

5454

5555
test-last-release:
56-
name: Integration Tests (Fossology 3.11.0)
56+
name: Integration Tests (Fossology 4.0.0)
5757
runs-on: ubuntu-latest
5858

5959
container:
@@ -63,7 +63,7 @@ jobs:
6363

6464
services:
6565
fossology:
66-
image: fossology/fossology:3.11.0
66+
image: fossology/fossology:4.0.0
6767
ports:
6868
- 8081:80
6969
volumes:

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ See `the OpenAPI specification <https://raw.githubusercontent.com/fossology/foss
2626

2727
- 1.2.1 (Fossology 3.10.0)
2828
- 1.3.2 (Fossology 3.11.0)
29-
- 1.3.4 (next Fossology release)
29+
- 1.4.0 (Fossology 4.0.0)
3030

3131
Documentation
3232
=============

docs-source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
copyright = "2021, Siemens AG"
2323

2424
# The full version, including major/minor/patch tags
25-
release = "1.4.0"
25+
release = "1.5.0"
2626

2727

2828
# -- General configuration ---------------------------------------------------

fossology/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ def detail_user(self, user_id):
283283
raise FossologyApiError(description, response)
284284

285285
def list_users(self):
286-
""" List all users from the Fossology instance
286+
"""List all users from the Fossology instance
287287
288288
API Endpoint: GET /users
289289
@@ -389,7 +389,9 @@ def search(
389389
raise FossologyApiError(description, response)
390390

391391
def filesearch(
392-
self, filelist: List = [], group: str = None,
392+
self,
393+
filelist: List = [],
394+
group: str = None,
393395
):
394396
"""Search for files from hash sum
395397

fossology/foss_cli.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,11 @@ def init_foss(ctx: dict):
222222
try:
223223
foss = Fossology(ctx.obj["SERVER"], ctx.obj["TOKEN"]) # using new API
224224
except AuthenticationError: # API version < 1.2.3 requires a username
225-
foss = Fossology(ctx.obj["SERVER"], ctx.obj["TOKEN"], name=ctx.obj["USERNAME"],)
225+
foss = Fossology(
226+
ctx.obj["SERVER"],
227+
ctx.obj["TOKEN"],
228+
name=ctx.obj["USERNAME"],
229+
)
226230
ctx.obj["FOSS"] = foss
227231
ctx.obj["USER"] = foss.user.name
228232
logger.debug(f"Logged in as user {foss.user.name}")
@@ -233,7 +237,10 @@ def init_foss(ctx: dict):
233237
@click.group()
234238
@click.option("--token", "-t", help="The token to be used.")
235239
@click.option(
236-
"--verbose", "-v", count=True, help="Increase verbosity level (e.g. -v -vv).",
240+
"--verbose",
241+
"-v",
242+
count=True,
243+
help="Increase verbosity level (e.g. -v -vv).",
237244
)
238245
@click.option(
239246
"--log_to_console/--no_log_to_console",
@@ -279,7 +286,7 @@ def cli(
279286
debug: bool,
280287
result_dir: str,
281288
):
282-
"""The foss_cli cmdline. Multiple -v increase verbosity-level. """
289+
"""The foss_cli cmdline. Multiple -v increase verbosity-level."""
283290
if log_to_console:
284291
console_handler = logging.StreamHandler()
285292
console_handler.setFormatter(formatter)
@@ -443,12 +450,12 @@ def config(
443450
@click.pass_context
444451
def log(ctx: click.core.Context, log_level: int, message_text: str):
445452
"""Add a Log Message to the log. If a log message is printed to the log depends
446-
on the verbosity defined starting the foss_cli (default level 0 /-v level 1/-vv level 2).
447-
Beeing on global verbosity level 0 only messages of --log_level 2 will be printed.
448-
Beeing on global verbosity level 1 messages of --log_level 1 and 2 will be printed.
449-
Beeing on global verbosity level 2 messages of --log_level 0,1,2 will be printed.
450-
Where the log messages are printed depends on the global configuration for --log_to_console,
451-
--log_to_file and --log_file_name.
453+
on the verbosity defined starting the foss_cli (default level 0 /-v level 1/-vv level 2).
454+
Beeing on global verbosity level 0 only messages of --log_level 2 will be printed.
455+
Beeing on global verbosity level 1 messages of --log_level 1 and 2 will be printed.
456+
Beeing on global verbosity level 2 messages of --log_level 0,1,2 will be printed.
457+
Where the log messages are printed depends on the global configuration for --log_to_console,
458+
--log_to_file and --log_file_name.
452459
"""
453460

454461
if log_level == 0:
@@ -514,7 +521,8 @@ def create_group(ctx: click.core.Context, group_name: str):
514521

515522
@cli.command("upload_file")
516523
@click.argument(
517-
"upload_file", type=click.Path(exists=True),
524+
"upload_file",
525+
type=click.Path(exists=True),
518526
)
519527
@click.option(
520528
"--folder_name", default="", show_default=True, help="The name of the upload folder"
@@ -596,7 +604,8 @@ def upload_file(
596604

597605
@cli.command("start_workflow")
598606
@click.argument(
599-
"file_name", type=click.Path(exists=True),
607+
"file_name",
608+
type=click.Path(exists=True),
600609
)
601610
@click.option(
602611
"--folder_name",

fossology/obj.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,11 @@ class Findings(object):
311311
"""
312312

313313
def __init__(
314-
self, scanner, conclusion, copyright=None, **kwargs,
314+
self,
315+
scanner,
316+
conclusion,
317+
copyright=None,
318+
**kwargs,
315319
):
316320
self.scanner = scanner
317321
self.conclusion = conclusion
@@ -478,7 +482,10 @@ class Licenses(object):
478482
"""
479483

480484
def __init__(
481-
self, filePath, findings=None, **kwargs,
485+
self,
486+
filePath,
487+
findings=None,
488+
**kwargs,
482489
):
483490
self.filepath = filePath
484491
if findings:
@@ -517,7 +524,12 @@ class Hash(object):
517524
"""
518525

519526
def __init__(
520-
self, sha1, md5, sha256, size, **kwargs,
527+
self,
528+
sha1,
529+
md5,
530+
sha256,
531+
size,
532+
**kwargs,
521533
):
522534
self.sha1 = sha1
523535
self.md5 = md5
@@ -548,7 +560,10 @@ class File(object):
548560
"""
549561

550562
def __init__(
551-
self, hash, findings, **kwargs,
563+
self,
564+
hash,
565+
findings,
566+
**kwargs,
552567
):
553568
self.hash = Hash.from_json(hash)
554569
self.findings = Findings.from_json(findings)

fossology/report.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,21 @@ def generate_report(
6565
description = f"Report generation for upload {upload.uploadname} failed"
6666
raise FossologyApiError(description, response)
6767

68-
@retry(retry=retry_if_exception_type(TryAgain), stop=stop_after_attempt(3))
69-
def download_report(self, report_id: int, group: str = None) -> Tuple[str, str]:
68+
@retry(retry=retry_if_exception_type(TryAgain), stop=stop_after_attempt(10))
69+
def download_report(
70+
self, report_id: int, group: str = None, wait_time: int = 0
71+
) -> Tuple[str, str]:
7072
"""Download a report
7173
7274
API Endpoint: GET /report/{id}
7375
76+
Get report for a given upload. If the report is not ready wait another ``wait_time`` seconds or look at
77+
the ``Retry-After`` to determine how long the wait period shall be.
78+
79+
If ``wait_time`` is 0, the time interval specified by the ``Retry-After`` header is used.
80+
81+
The function stops trying after **10 attempts**.
82+
7483
:Example:
7584
7685
>>> from fossology import Fossology
@@ -79,19 +88,22 @@ def download_report(self, report_id: int, group: str = None) -> Tuple[str, str]:
7988
>>>
8089
>>> # Generate a report for upload 1
8190
>>> report_id = foss.generate_report(foss.detail_upload(1)) # doctest: +SKIP
82-
>>> report_content, report_name = foss.download_report(report_id) # doctest: +SKIP
91+
>>> # Wait up to 20 Minutes until the report is ready
92+
>>> report_content, report_name = foss.download_report(report_id, wait_time=120) # doctest: +SKIP
8393
>>> with open(report_name, "wb") as report_file:
8494
... report_file.write(report_content) # doctest: +SKIP
8595
8696
:param report_id: the id of the generated report
8797
:param group: the group name to choose while downloading a specific report (default: None)
98+
:param wait_time: use a customized upload wait time instead of Retry-After (in seconds, default: 0)
8899
:type report_id: int
89100
:type group: string
101+
:type wait_time: int
90102
:return: the report content and the report name
91103
:rtype: Tuple[str, str]
92104
:raises FossologyApiError: if the REST call failed
93105
:raises AuthorizationError: if the user can't access the group
94-
:raises TryAgain: if the report generation timed out after 3 retries
106+
:raises TryAgain: if the report generation times out after 10 retries
95107
"""
96108
headers = dict()
97109
if group:
@@ -100,17 +112,20 @@ def download_report(self, report_id: int, group: str = None) -> Tuple[str, str]:
100112
response = self.session.get(f"{self.api}/report/{report_id}", headers=headers)
101113
if response.status_code == 200:
102114
content = response.headers["Content-Disposition"]
103-
report_name_pattern = '(^attachment; filename=")(.*)("$)'
104-
report_name = re.match(report_name_pattern, content).group(2)
115+
report_name_pattern = "(^attachment; filename=)(\"|')?([^\"|']*)(\"|'$)?"
116+
report_name = re.match(report_name_pattern, content).group(3)
105117
return response.content, report_name
106118
elif response.status_code == 403:
107119
description = (
108120
f"Getting report {report_id} {get_options(group)}not authorized"
109121
)
110122
raise AuthorizationError(description, response)
111123
elif response.status_code == 503:
112-
wait_time = response.headers["Retry-After"]
113-
logger.debug(f"Retry get report after {wait_time} seconds")
124+
if not wait_time:
125+
wait_time = response.headers["Retry-After"]
126+
logger.debug(
127+
f"Retry GET report {report_id} after {wait_time} seconds: {response.json()['message']}"
128+
)
114129
time.sleep(int(wait_time))
115130
raise TryAgain
116131
else:

0 commit comments

Comments
 (0)