3030import time
3131import uuid
3232from dataclasses import dataclass
33- from enum import IntEnum
33+ from enum import Enum , IntEnum
3434from typing import Dict , Optional
3535from urllib .parse import urlparse
3636
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-
108108class ScanossGrpcError (Exception ):
109109 """
110110 Custom exception for SCANOSS gRPC errors
@@ -116,10 +116,19 @@ class ScanossGrpcError(Exception):
116116class 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
125134class 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