Skip to content

Commit cffcbf1

Browse files
committed
Updated PolicyCheck return codes and simplified duplicate code
1 parent a0fa832 commit cffcbf1

File tree

5 files changed

+96
-81
lines changed

5 files changed

+96
-81
lines changed

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ Calculates hashes for a directory or file and shows them on the STDOUT.
156156
- Fingerprint all hidden files/folders
157157

158158
-----------------------------------------
159-
Detect dependecies: dependencies, dp, dep
159+
Detect dependencies: dependencies, dp, dep
160160
-----------------------------------------
161161

162162
Scan source code for dependencies, but do not decorate them.

src/scanoss/inspection/policy_check.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class PolicyStatus(Enum):
3939
POLICY_FAIL (int): Indicates that the policy check failed (value: 1).
4040
ERROR (int): Indicates that an error occurred during the policy check (value: 2).
4141
"""
42-
4342
POLICY_SUCCESS = 0
4443
POLICY_FAIL = 2
4544
ERROR = 1
@@ -223,6 +222,39 @@ def _is_valid_format(self) -> bool:
223222
self.print_stderr(f'ERROR: Invalid format "{self.format_type}". Valid formats are: {valid_formats_str}')
224223
return False
225224
return True
225+
226+
def _generate_formatter_report(self, components: list[Dict]):
227+
"""
228+
Generates a formatted report for a given component based on the defined formatter.
229+
230+
Parameters:
231+
components (List[dict]): A list of dictionaries representing the components to be
232+
processed and formatted. Each dictionary contains detailed information that adheres
233+
to the format requirements for the specified formatter.
234+
235+
Returns:
236+
Tuple[int, dict]: A tuple where the first element represents the policy status code
237+
and the second element is a dictionary containing formatted results information,
238+
typically with keys 'details' and 'summary'.
239+
240+
Raises:
241+
KeyError: When a required key is missing from the provided component, causing the
242+
formatter to fail.
243+
ValueError: If an invalid component is passed and renders unable to process.
244+
"""
245+
# Get a formatter for the output results
246+
formatter = self._get_formatter()
247+
if formatter is None:
248+
return PolicyStatus.ERROR.value, {}
249+
# Format the results
250+
data = formatter(components)
251+
## Save outputs if required
252+
self.print_to_file_or_stdout(data['details'], self.output)
253+
self.print_to_file_or_stderr(data['summary'], self.status)
254+
# Check to see if we have policy violations
255+
if len(components) > 0:
256+
return PolicyStatus.POLICY_FAIL.value, data
257+
return PolicyStatus.POLICY_SUCCESS.value, data
226258
#
227259
# End of PolicyCheck Class
228260
#

src/scanoss/inspection/raw/copyleft.py

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def __init__( # noqa: PLR0913
6464
explicit: str = None,
6565
):
6666
"""
67-
Initialize the Copyleft class.
67+
Initialise the Copyleft class.
6868
6969
:param debug: Enable debug mode
7070
:param trace: Enable trace mode (default True)
@@ -111,25 +111,7 @@ def _markdown(self, components: list[Component]) -> Dict[str, Any]:
111111
:param components: List of components with copyleft licenses
112112
:return: Dictionary with formatted Markdown details and summary
113113
"""
114-
# A component is considered unique by its combination of PURL (Package URL) and license
115-
component_licenses = self._group_components_by_license(components)
116-
headers = ['Component', 'License', 'URL', 'Copyleft']
117-
centered_columns = [1, 4]
118-
rows: [[]] = []
119-
for comp_lic_item in component_licenses:
120-
row = [
121-
comp_lic_item['purl'],
122-
comp_lic_item['spdxid'],
123-
comp_lic_item['url'],
124-
'YES' if comp_lic_item['copyleft'] else 'NO',
125-
]
126-
rows.append(row)
127-
# End license loop
128-
# End component loop
129-
return {
130-
'details': f'### Copyleft licenses\n{self.generate_table(headers, rows, centered_columns)}\n',
131-
'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
132-
}
114+
return self._md_summary_generator(components, self.generate_table)
133115

134116
def _jira_markdown(self, components: list[Component]) -> Dict[str, Any]:
135117
"""
@@ -138,27 +120,51 @@ def _jira_markdown(self, components: list[Component]) -> Dict[str, Any]:
138120
:param components: List of components with copyleft licenses
139121
:return: Dictionary with formatted Markdown details and summary
140122
"""
123+
return self._md_summary_generator(components, self.generate_jira_table)
124+
125+
def _md_summary_generator(self, components: list[Component], table_generator):
126+
"""
127+
Generates a Markdown summary for components with a focus on copyleft licenses.
128+
129+
This function processes a list of components and groups them by their licenses.
130+
For each group, the components are mapped with their license data and a tabular representation is created.
131+
The generated Markdown summary includes a detailed table and a summary overview.
132+
133+
Parameters:
134+
components: list[Component]
135+
A list of Component objects to process for generating the summary.
136+
table_generator
137+
A callable function to generate tabular data for components.
138+
139+
Returns:
140+
dict
141+
A dictionary containing two keys:
142+
- 'details': A detailed Markdown representation including a table of components
143+
and associated copyleft license data.
144+
- 'summary': A textual summary highlighting the total number of components
145+
with copyleft licenses.
146+
"""
141147
# A component is considered unique by its combination of PURL (Package URL) and license
142148
component_licenses = self._group_components_by_license(components)
143149
headers = ['Component', 'License', 'URL', 'Copyleft']
144150
centered_columns = [1, 4]
145-
rows: [[]] = []
151+
rows = []
146152
for comp_lic_item in component_licenses:
147-
row = [
148-
comp_lic_item['purl'],
149-
comp_lic_item['spdxid'],
150-
comp_lic_item['url'],
151-
'YES' if comp_lic_item['copyleft'] else 'NO',
152-
]
153-
rows.append(row)
154-
# End license loop
153+
row = [
154+
comp_lic_item['purl'],
155+
comp_lic_item['spdxid'],
156+
comp_lic_item['url'],
157+
'YES' if comp_lic_item['copyleft'] else 'NO',
158+
]
159+
rows.append(row)
160+
# End license loop
155161
# End component loop
156162
return {
157-
'details': f'{self.generate_jira_table(headers, rows, centered_columns)}',
163+
'details': f'### Copyleft Licenses\n{table_generator(headers, rows, centered_columns)}.\n',
158164
'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
159165
}
160166

161-
def _get_components_with_copyleft_licenses(self, components: list) -> list[Component]:
167+
def _get_components_with_copyleft_licenses(self, components: list) -> list[Dict]:
162168
"""
163169
Filter the components list to include only those with copyleft licenses.
164170
@@ -223,21 +229,8 @@ def run(self):
223229
return PolicyStatus.ERROR.value, {}
224230
# Get a list of copyleft components if they exist
225231
copyleft_components = self._get_components_with_copyleft_licenses(components)
226-
# Get a formatter for the output results
227-
formatter = self._get_formatter()
228-
if formatter is None:
229-
return PolicyStatus.ERROR.value, {}
230-
# Format the results
231-
data = formatter(copyleft_components)
232-
## Save outputs if required
233-
self.print_to_file_or_stdout(data['details'], self.output)
234-
self.print_to_file_or_stderr(data['summary'], self.status)
235-
# Check to see if we have policy violations
236-
if len(copyleft_components) <= 0:
237-
return PolicyStatus.POLICY_FAIL.value, data
238-
return PolicyStatus.POLICY_SUCCESS.value, data
239-
240-
232+
# Format the results and save to files if required
233+
return self._generate_formatter_report(copyleft_components)
241234
#
242235
# End of Copyleft Class
243236
#

src/scanoss/inspection/raw/undeclared_component.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def _markdown(self, components: list[Component]) -> Dict[str, Any]:
187187
:return: Dictionary with formatted Markdown details and summary
188188
"""
189189
headers = ['Component', 'License']
190-
rows: [[]] = []
190+
rows = []
191191
# TODO look at using SpdxLite license name lookup method
192192
component_licenses = self._group_components_by_license(components)
193193
for component in component_licenses:
@@ -205,7 +205,7 @@ def _jira_markdown(self, components: list) -> Dict[str, Any]:
205205
:return: Dictionary with formatted Markdown details and summary
206206
"""
207207
headers = ['Component', 'License']
208-
rows: [[]] = []
208+
rows = []
209209
# TODO look at using SpdxLite license name lookup method
210210
component_licenses = self._group_components_by_license(components)
211211
for component in component_licenses:
@@ -295,23 +295,13 @@ def run(self):
295295
components = self._get_components()
296296
if components is None:
297297
return PolicyStatus.ERROR.value, {}
298-
# Get undeclared component summary (if any)
298+
# Get an undeclared component summary (if any)
299299
undeclared_components = self._get_undeclared_components(components)
300300
if undeclared_components is None:
301301
return PolicyStatus.ERROR.value, {}
302302
self.print_debug(f'Undeclared components: {undeclared_components}')
303-
formatter = self._get_formatter()
304-
if formatter is None:
305-
return PolicyStatus.ERROR.value, {}
306-
data = formatter(undeclared_components)
307-
# Output the results
308-
self.print_to_file_or_stdout(data['details'], self.output)
309-
self.print_to_file_or_stderr(data['summary'], self.status)
310-
# Determine if the filter found results or not
311-
if len(undeclared_components) <= 0:
312-
return PolicyStatus.POLICY_FAIL.value, data
313-
return PolicyStatus.POLICY_SUCCESS.value, data
314-
303+
# Format the results and save to files if required
304+
return self._generate_formatter_report(undeclared_components)
315305
#
316306
# End of UndeclaredComponent Class
317307
#

tests/test_policy_inspect.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,9 @@ def test_dependency_track_project_violation_json_formatter(self, mock_service):
441441
mock_service.return_value = Mock()
442442
project_violation = DependencyTrackProjectViolationPolicyCheck(
443443
format_type='json',
444-
dependency_track_api_key='test_key',
445-
dependency_track_url='http://localhost',
446-
dependency_track_project_id='test_project'
444+
api_key='test_key',
445+
url='http://localhost',
446+
project_id='test_project'
447447
)
448448
test_violations = [
449449
{
@@ -476,9 +476,9 @@ def test_dependency_track_project_violation_markdown_formatter(self, mock_servic
476476
mock_service.return_value = Mock()
477477
project_violation = DependencyTrackProjectViolationPolicyCheck(
478478
format_type='md',
479-
dependency_track_api_key='test_key',
480-
dependency_track_url='http://localhost',
481-
dependency_track_project_id='test_project'
479+
api_key='test_key',
480+
url='http://localhost',
481+
project_id='test_project'
482482
)
483483
test_violations = [
484484
{
@@ -512,9 +512,9 @@ def test_dependency_track_project_violation_markdown_formatter(self, mock_servic
512512
def test_dependency_track_project_violation_sort_violations(self, mock_service):
513513
mock_service.return_value = Mock()
514514
project_violation = DependencyTrackProjectViolationPolicyCheck(
515-
dependency_track_api_key='test_key',
516-
dependency_track_url='http://localhost',
517-
dependency_track_project_id='test_project'
515+
api_key='test_key',
516+
url='http://localhost',
517+
project_id='test_project'
518518
)
519519
test_violations = [
520520
{'type': 'LICENSE', 'uuid': 'license-violation'},
@@ -533,9 +533,9 @@ def test_dependency_track_project_violation_empty_violations(self, mock_service)
533533
mock_service.return_value = Mock()
534534
project_violation = DependencyTrackProjectViolationPolicyCheck(
535535
format_type='json',
536-
dependency_track_api_key='test_key',
537-
dependency_track_url='http://localhost',
538-
dependency_track_project_id='test_project'
536+
api_key='test_key',
537+
url='http://localhost',
538+
project_id='test_project'
539539
)
540540
empty_violations = []
541541
result = project_violation._json(empty_violations)
@@ -548,9 +548,9 @@ def test_dependency_track_project_violation_markdown_empty(self, mock_service):
548548
mock_service.return_value = Mock()
549549
project_violation = DependencyTrackProjectViolationPolicyCheck(
550550
format_type='md',
551-
dependency_track_api_key='test_key',
552-
dependency_track_url='http://localhost',
553-
dependency_track_project_id='test_project'
551+
api_key='test_key',
552+
url='http://localhost',
553+
project_id='test_project'
554554
)
555555
empty_violations = []
556556
result = project_violation._markdown(empty_violations)
@@ -563,9 +563,9 @@ def test_dependency_track_project_violation_multiple_types(self, mock_service):
563563
mock_service.return_value = Mock()
564564
project_violation = DependencyTrackProjectViolationPolicyCheck(
565565
format_type='json',
566-
dependency_track_api_key='test_key',
567-
dependency_track_url='http://localhost',
568-
dependency_track_project_id='test_project'
566+
api_key='test_key',
567+
url='http://localhost',
568+
project_id='test_project'
569569
)
570570
test_violations = [
571571
{

0 commit comments

Comments
 (0)