Skip to content

Commit 3ad08fb

Browse files
author
Glenn Snyder
authored
Merge pull request #80 from blackducksoftware/gsnyder/issue-79-re-generating-notices-report-in-html-format
fixing issue 79 regarding generating notices report in JSON format
2 parents 84a2569 + fba9308 commit 3ad08fb

File tree

3 files changed

+71
-27
lines changed

3 files changed

+71
-27
lines changed

blackduck/HubRestApi.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ def _get_policy_url(self):
424424

425425
def get_policies(self, parameters={}):
426426
url = self._get_policy_url() + self._get_parameter_string(parameters)
427-
headers = {'Accept': 'application/vnd.blackducksoftware.policy-4+json'}
427+
headers = {'Accept': 'application/json'}
428428
response = self.execute_get(url, custom_headers=headers)
429429
return response.json()
430430

@@ -540,12 +540,12 @@ def create_version_reports(self, version, report_list, format="CSV"):
540540
version_reports_url = self.get_link(version, 'versionReport')
541541
return self.execute_post(version_reports_url, post_data)
542542

543-
valid_notices_formats = ["TEXT", "HTML"]
543+
valid_notices_formats = ["TEXT", "JSON"]
544544
def create_version_notices_report(self, version, format="TEXT"):
545545
assert format in HubInstance.valid_notices_formats, "Format must be one of {}".format(HubInstance.valid_notices_formats)
546546

547547
post_data = {
548-
'categories': HubInstance.valid_categories,
548+
'categories': ["COPYRIGHT_TEXT"],
549549
'versionId': version['_meta']['href'].split("/")[-1],
550550
'reportType': 'VERSION_LICENSE',
551551
'reportFormat': format
@@ -554,9 +554,28 @@ def create_version_notices_report(self, version, format="TEXT"):
554554
return self.execute_post(notices_report_url, post_data)
555555

556556
def download_report(self, report_id):
557+
# TODO: Fix me, looks like the reports should be downloaded from different paths than the one here, and depending on the type and format desired the path can change
557558
url = self.get_urlbase() + "/api/reports/{}".format(report_id)
558559
return self.execute_get(url, {'Content-Type': 'application/zip', 'Accept':'application/zip'})
559560

561+
def download_notification_report(self, report_location_url):
562+
'''Download the notices report using the report URL. Inspect the report object to determine
563+
the format and use the appropriate media header'''
564+
custom_headers = {'Accept': 'application/vnd.blackducksoftware.report-4+json'}
565+
response = self.execute_get(report_location_url, custom_headers=custom_headers)
566+
report_obj = response.json()
567+
568+
if report_obj['reportFormat'] == 'TEXT':
569+
download_url = self.get_link(report_obj, "download") + ".json"
570+
logging.debug("downloading report from {}".format(download_url))
571+
response = self.execute_get(download_url, {'Accept': 'application/zip'})
572+
else:
573+
# JSON
574+
contents_url = self.get_link(report_obj, "content")
575+
logging.debug("retrieving report contents from {}".format(contents_url))
576+
response = self.execute_get(contents_url, {'Accept': 'application/json'})
577+
return response, report_obj['reportFormat']
578+
560579
##
561580
#
562581
# (Global) Vulnerability reports

examples/generate_notices_report_for_project_version.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,51 @@
1010

1111
import argparse
1212
import json
13+
import logging
14+
import sys
1315
import time
1416
import zipfile
1517

1618
parser = argparse.ArgumentParser("A program to generate the notices file for a given project-version")
1719
parser.add_argument("project_name")
1820
parser.add_argument("version_name")
19-
parser.add_argument("--zip_file_name", default="notices_report.zip")
20-
parser.add_argument("--reports",
21-
default="version,scans,components,vulnerabilities,source",
22-
help="Comma separated list (no spaces) of the reports to generate - version, scans, components, vulnerabilities, source, and cryptography reports (default: all, except cryptography")
23-
parser.add_argument('--format', default='TEXT', choices=["HTML", "TEXT"], help="Report format - choices are TEXT or HTML")
21+
parser.add_argument('-f', "--file_name_base", default="notices_report", help="Base file name to write the report data into. If the report format is TEXT a .zip file will be created, otherwise a .json file")
22+
parser.add_argument('-r', '--report_format', default='TEXT', choices=["JSON", "TEXT"], help="Report format - choices are TEXT or HTML")
2423

2524
args = parser.parse_args()
2625

2726
hub = HubInstance()
2827

29-
# TODO: Promote this to the API?
28+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', stream=sys.stderr, level=logging.DEBUG)
29+
3030
class FailedReportDownload(Exception):
3131
pass
3232

33-
def download_report(location, filename, retries=4):
33+
def download_report(location, file_name_base, retries=10):
3434
report_id = location.split("/")[-1]
3535

3636
if retries:
37-
print("Retrieving generated report from {}".format(location))
38-
response = hub.download_report(report_id)
37+
logging.debug("Retrieving generated report from {}".format(location))
38+
# response = hub.download_report(report_id)
39+
response, report_format = hub.download_notification_report(location)
3940
if response.status_code == 200:
40-
with open(filename, "wb") as f:
41-
f.write(response.content)
42-
print("Successfully downloaded zip file to {} for report {}".format(filename, report_id))
41+
if report_format == "TEXT":
42+
filename = file_name_base + ".zip"
43+
with open(filename, "wb") as f:
44+
f.write(response.content)
45+
else:
46+
# JSON format
47+
filename = file_name_base + ".json"
48+
with open(filename, "w") as f:
49+
json.dump(response.json(), f, indent=3)
50+
logging.info("Successfully downloaded json file to {} for report {}".format(
51+
filename, report_id))
4352
else:
44-
print("Failed to retrieve report {}".format(report_id))
45-
print("Probably not ready yet, waiting 5 seconds then retrying...")
53+
logging.warning("Failed to retrieve report {}".format(report_id))
54+
logging.warning("Probably not ready yet, waiting 5 seconds then retrying (remaining retries={}".format(retries))
4655
time.sleep(5)
4756
retries -= 1
48-
download_report(location, filename, retries)
57+
download_report(location, file_name_base, retries)
4958
else:
5059
raise FailedReportDownload("Failed to retrieve report {} after multiple retries".format(report_id))
5160

@@ -54,22 +63,23 @@ def download_report(location, filename, retries=4):
5463
if project:
5564
version = hub.get_version_by_name(project, args.version_name)
5665

57-
response = hub.create_version_notices_report(version, args.format)
66+
response = hub.create_version_notices_report(version, args.report_format)
5867

5968
if response.status_code == 201:
60-
print("Successfully created reports ({}) for project {} and version {}".format(
61-
args.reports, args.project_name, args.version_name))
69+
logging.info("Successfully created notices report in {} format for project {} and version {}".format(
70+
args.report_format, args.project_name, args.version_name))
6271
location = response.headers['Location']
63-
download_report(location, args.zip_file_name)
72+
download_report(location, args.file_name_base)
73+
6474

6575
# Showing how you can interact with the downloaded zip and where to find the
6676
# output content. Uncomment the lines below to see how it works.
6777

68-
# with zipfile.ZipFile(zip_file_name, 'r') as zipf:
78+
# with zipfile.ZipFile(zip_file_name_base, 'r') as zipf:
6979
# with zipf.open("{}/{}/version-license.txt".format(args.project_name, args.version_name), "r") as license_file:
7080
# print(license_file.read())
7181
else:
72-
print("Failed to create reports for project {} version {}, status code returned {}".format(
82+
logging.error("Failed to create reports for project {} version {}, status code returned {}".format(
7383
args.project_name, args.version_name, response.status_code))
7484
else:
75-
print("Did not find project with name {}".format(args.project_name))
85+
logging.warning("Did not find project with name {}".format(args.project_name))

examples/get_bom_components.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@
55
import json
66
import logging
77
import sys
8-
import timestring
98

109
from blackduck.HubRestApi import HubInstance
1110

1211
parser = argparse.ArgumentParser("Retreive BOM component info for the given project and version")
1312
parser.add_argument("project_name")
1413
parser.add_argument("version")
1514
group = parser.add_mutually_exclusive_group()
15+
group.add_argument("-l", "--limit", default=10, help="Set limit on number of components to retrieve")
1616
group.add_argument("-u", "--unreviewed", action='store_true')
1717
group.add_argument("-r", "--reviewed", action='store_true')
1818
parser.add_argument("-v", "--vulnerabilities", action='store_true', help="Get the vulnerability info for each of the components")
19+
parser.add_argument("-c", "--custom_fields", action='store_true', help="Get the custom field info for each of the components")
1920

2021
args = parser.parse_args()
2122

@@ -31,7 +32,11 @@
3132

3233
components_url = hub.get_link(version, "components")
3334

34-
response = hub.execute_get(components_url)
35+
components_url += "?limit={}".format(args.limit)
36+
37+
custom_headers = {'Accept': 'application/vnd.blackducksoftware.bill-of-materials-6+json'}
38+
39+
response = hub.execute_get(components_url, custom_headers=custom_headers)
3540
if response.status_code == 200:
3641
components = response.json()
3742
components = components.get('items', [])
@@ -49,4 +54,14 @@
4954
if response.status_code == 200:
5055
vulnerabilities = response.json().get('items', [])
5156
component['vulnerabilities'] = vulnerabilities
57+
58+
if args.custom_fields:
59+
for component in components:
60+
custom_fields_url = hub.get_link(component, "custom-fields")
61+
response = hub.execute_get(custom_fields_url, custom_headers=custom_headers)
62+
custom_fields = []
63+
if response.status_code == 200:
64+
custom_fields = response.json().get('items', [])
65+
component['custom_fields'] = custom_fields
66+
5267
print(json.dumps(components))

0 commit comments

Comments
 (0)