Skip to content

Commit 98588f0

Browse files
committed
[SP-3346] fix: license service api call
1 parent 6ff6c24 commit 98588f0

File tree

3 files changed

+68
-35
lines changed

3 files changed

+68
-35
lines changed

src/scanoss/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,6 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
318318
description=f'Show License details: {__version__}',
319319
help='Retrieve licenses for the given components',
320320
)
321-
c_licenses.add_argument('--grpc', action='store_true', help='Enable gRPC support')
322321
c_licenses.set_defaults(func=comp_licenses)
323322

324323
# Component Sub-command: component semgrep
@@ -1069,6 +1068,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
10691068
c_provenance,
10701069
c_search,
10711070
c_versions,
1071+
c_licenses,
10721072
]:
10731073
p.add_argument('--grpc', action='store_true', help='Enable gRPC support')
10741074

src/scanoss/components.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def get_licenses(self, json_file: str = None, purls: [] = None, output_file: str
387387

388388
# We'll use the new ComponentBatchRequest instead of deprecated PurlRequest for the license api
389389
component_batch_request = {'components': purls_request.get('purls')}
390-
response = self.grpc_api.get_licenses(component_batch_request)
390+
response = self.grpc_api.get_licenses(component_batch_request, use_grpc=self.use_grpc)
391391
if response:
392392
print(json.dumps(response, indent=2, sort_keys=True), file=file)
393393
success = True

src/scanoss/scanossgrpc.py

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import time
3131
import uuid
3232
from dataclasses import dataclass
33-
from enum import IntEnum
33+
from enum import Enum, IntEnum
3434
from typing import Dict, Optional
3535
from urllib.parse import urlparse
3636

@@ -100,11 +100,11 @@
100100
'method': 'POST',
101101
},
102102
'geoprovenance.GetOriginByComponents': {'path': '/geoprovenance/origin/components', 'method': 'POST'},
103+
'licenses.GetComponentsLicenses': {'path': '/licenses/components', 'method': 'POST'},
103104
'semgrep.GetComponentsIssues': {'path': '/semgrep/issues/components', 'method': 'POST'},
104105
'scanning.FolderHashScan': {'path': '/scanning/hfh/scan', 'method': 'POST'},
105106
}
106107

107-
108108
class ScanossGrpcError(Exception):
109109
"""
110110
Custom exception for SCANOSS gRPC errors
@@ -116,10 +116,19 @@ class ScanossGrpcError(Exception):
116116
class ScanossGrpcStatusCode(IntEnum):
117117
"""Status codes for SCANOSS gRPC responses"""
118118

119+
UNSPECIFIED = 0
119120
SUCCESS = 1
120-
SUCCESS_WITH_WARNINGS = 2
121-
FAILED_WITH_WARNINGS = 3
121+
SUCCEEDED_WITH_WARNINGS = 2
122+
WARNING = 3
122123
FAILED = 4
124+
125+
class ScanossRESTStatusCode(Enum):
126+
"""Status codes for SCANOSS REST responses"""
127+
UNSPECIFIED = 'UNSPECIFIED'
128+
SUCCESS = 'SUCCESS'
129+
SUCCEEDED_WITH_WARNINGS = 'SUCCEEDED_WITH_WARNINGS'
130+
WARNING = 'WARNING'
131+
FAILED = 'FAILED'
123132

124133

125134
class ScanossGrpc(ScanossBase):
@@ -495,13 +504,13 @@ def _call_rpc(self, rpc_method, request_input, request_type, debug_msg: Optional
495504
raise ScanossGrpcError(
496505
f'{e.__class__.__name__} while sending gRPC message (rqId: {request_id}): {e.details()}'
497506
)
498-
if resp and not self._check_status_response(resp.status, request_id):
507+
if resp and not self._check_status_response_grpc(resp.status, request_id):
499508
return None
500509

501510
resp_dict = MessageToDict(resp, preserving_proto_field_name=True)
502511
return resp_dict
503512

504-
def _check_status_response(self, status_response: StatusResponse, request_id: str = None) -> bool:
513+
def _check_status_response_grpc(self, status_response: StatusResponse, request_id: str = None) -> bool:
505514
"""
506515
Check the response object to see if the command was successful or not
507516
:param status_response: Status Response
@@ -516,15 +525,57 @@ def _check_status_response(self, status_response: StatusResponse, request_id: st
516525
if status_code > ScanossGrpcStatusCode.SUCCESS:
517526
ret_val = False # default to failed
518527
msg = 'Unsuccessful'
519-
if status_code == ScanossGrpcStatusCode.SUCCESS_WITH_WARNINGS:
528+
if status_code == ScanossGrpcStatusCode.SUCCEEDED_WITH_WARNINGS:
520529
msg = 'Succeeded with warnings'
521530
ret_val = True # No need to fail as it succeeded with warnings
522-
elif status_code == ScanossGrpcStatusCode.FAILED_WITH_WARNINGS:
531+
elif status_code == ScanossGrpcStatusCode.WARNING:
523532
msg = 'Failed with warnings'
524533
self.print_stderr(f'{msg} (rqId: {request_id} - status: {status_code}): {status_response.message}')
525534
return ret_val
526535
return True
527536

537+
def check_status_response_rest(self, status_dict: dict, request_id: str = None) -> bool:
538+
"""
539+
Check the REST response dictionary to see if the command was successful or not
540+
541+
Args:
542+
status_dict (dict): Status dictionary from REST response containing 'status' and 'message' keys
543+
request_id (str, optional): Request ID for logging
544+
Returns:
545+
bool: True if successful, False otherwise
546+
"""
547+
if not status_dict:
548+
self.print_stderr(f'Warning: No status response supplied (rqId: {request_id}). Assuming it was ok.')
549+
return True
550+
551+
self.print_debug(f'Checking response status (rqId: {request_id}): {status_dict}')
552+
553+
# Get status from dictionary - it can be either a string or nested dict
554+
status = status_dict.get('status')
555+
message = status_dict.get('message', '')
556+
557+
# Handle case where status might be a string directly
558+
if isinstance(status, str):
559+
status_str = status.upper()
560+
if status_str == ScanossRESTStatusCode.SUCCESS.value:
561+
return True
562+
elif status_str == ScanossRESTStatusCode.SUCCEEDED_WITH_WARNINGS.value:
563+
self.print_stderr(f'Succeeded with warnings (rqId: {request_id}): {message}')
564+
return True
565+
elif status_str == ScanossRESTStatusCode.WARNING.value:
566+
self.print_stderr(f'Failed with warnings (rqId: {request_id}): {message}')
567+
return False
568+
elif status_str == ScanossRESTStatusCode.FAILED.value:
569+
self.print_stderr(f'Unsuccessful (rqId: {request_id}): {message}')
570+
return False
571+
else:
572+
self.print_debug(f'Unknown status "{status_str}" (rqId: {request_id}). Assuming success.')
573+
return True
574+
575+
# Otherwise asume success
576+
self.print_debug(f'Unexpected status type {type(status)} (rqId: {request_id}). Assuming success.')
577+
return True
578+
528579
def _get_proxy_config(self):
529580
"""
530581
Set the grpc_proxy/http_proxy/https_proxy environment variables if PAC file has been specified
@@ -686,7 +737,7 @@ def get_versions_in_range_for_purl(self, request: Dict, use_grpc: bool = None) -
686737
use_grpc=use_grpc,
687738
)
688739

689-
def get_licenses(self, request: Dict) -> Optional[Dict]:
740+
def get_licenses(self, request: Dict, use_grpc: bool = None) -> Optional[Dict]:
690741
"""
691742
Client function to call the rpc for Licenses GetComponentsLicenses
692743
It will either use REST (default) or gRPC depending on the use_grpc flag
@@ -695,26 +746,14 @@ def get_licenses(self, request: Dict) -> Optional[Dict]:
695746
request (Dict): ComponentsRequest
696747
Returns:
697748
Optional[Dict]: ComponentsLicenseResponse, or None if the request was not successfull
698-
"""
699-
if self.use_grpc:
700-
return self._get_licenses_grpc(request)
701-
else:
702-
return self._get_licenses_rest(request)
703-
704-
def _get_licenses_grpc(self, request: Dict) -> Optional[Dict]:
705-
"""
706-
Client function to call the rpc for GetComponentsLicenses
707-
708-
Args:
709-
request (Dict): ComponentsRequest
710-
Returns:
711-
Optional[Dict]: ComponentsLicenseResponse, or None if the request was not successfull
712-
"""
713-
return self._call_rpc(
749+
"""
750+
return self._call_api(
751+
'licenses.GetComponentsLicenses',
714752
self.license_stub.GetComponentsLicenses,
715753
request,
716754
ComponentsRequest,
717755
'Sending data for license decoration (rqId: {rqId})...',
756+
use_grpc=use_grpc,
718757
)
719758

720759
def load_generic_headers(self, url: str = None):
@@ -860,14 +899,8 @@ def _call_rest(self, endpoint_key: str, request_input: dict, debug_msg: Optional
860899
else: # POST
861900
response = self._rest_post(endpoint_url, request_id, request_input)
862901

863-
# Check for status in response and validate
864-
if response and 'status' in response:
865-
status_dict = response.get('status', {})
866-
if status_dict.get('status') != 'SUCCESS':
867-
self.print_stderr(f'Request failed (rqId: {request_id}): {status_dict.get("message", "Unknown error")}')
868-
if status_dict.get('status') in ['FAILED', 'FAILED_WITH_WARNINGS']:
869-
return None
870-
del response['status']
902+
if response and 'status' in response and not self.check_status_response_rest(response['status'], request_id):
903+
return None
871904

872905
return response
873906

0 commit comments

Comments
 (0)