2424from http import HTTPStatus
2525from typing import Optional
2626import json
27+ import re
2728from urllib .parse import unquote
28- from requests import HTTPError , RequestException
2929import requests .utils
3030
3131from sonar import platform
@@ -89,16 +89,11 @@ def get_object(cls, concerned_object: projects.Project, branch_name: str) -> Bra
8989 o = Branch .CACHE .get (concerned_object .key , branch_name , concerned_object .base_url ())
9090 if o :
9191 return o
92- try :
93- data = json .loads (concerned_object .get (Branch .API [c .LIST ], params = {"project" : concerned_object .key }).text )
94- except (ConnectionError , RequestException ) as e :
95- util .handle_error (e , f"searching { str (concerned_object )} for branch '{ branch_name } '" , catch_http_statuses = (HTTPStatus .NOT_FOUND ,))
96- raise exceptions .ObjectNotFound (concerned_object .key , f"{ str (concerned_object )} not found" )
97-
98- for br in data .get ("branches" , []):
99- if br ["name" ] == branch_name :
100- return cls .load (concerned_object , branch_name , br )
101- raise exceptions .ObjectNotFound (branch_name , f"Branch '{ branch_name } ' of { str (concerned_object )} not found" )
92+ data = json .loads (concerned_object .get (Branch .API [c .LIST ], params = {"project" : concerned_object .key }).text )
93+ br = next ((b for b in data .get ("branches" , []) if b ["name" ] == branch_name ), None )
94+ if not br :
95+ raise exceptions .ObjectNotFound (branch_name , f"Branch '{ branch_name } ' of { str (concerned_object )} not found" )
96+ return cls .load (concerned_object , branch_name , br )
10297
10398 @classmethod
10499 def load (cls , concerned_object : projects .Project , branch_name : str , data : types .ApiPayload ) -> Branch :
@@ -112,9 +107,11 @@ def load(cls, concerned_object: projects.Project, branch_name: str, data: types.
112107 """
113108 branch_name = unquote (branch_name )
114109 o = Branch .CACHE .get (concerned_object .key , branch_name , concerned_object .base_url ())
110+ br_data = next ((br for br in data .get ("branches" , []) if br ["name" ] == branch_name ), None )
115111 if not o :
116112 o = cls (concerned_object , branch_name )
117- o ._load (data )
113+ if br_data :
114+ o ._load (br_data )
118115 return o
119116
120117 def __str__ (self ) -> str :
@@ -135,44 +132,33 @@ def refresh(self) -> Branch:
135132 :return: itself
136133 :rtype: Branch
137134 """
138- try :
139- data = json .loads (self .get (Branch .API [c .LIST ], params = self .api_params (c .LIST )).text )
140- except (ConnectionError , RequestException ) as e :
141- util .handle_error (e , f"refreshing { str (self )} " , catch_http_statuses = (HTTPStatus .NOT_FOUND ,))
142- Branch .CACHE .pop (self )
143- raise exceptions .ObjectNotFound (self .key , f"{ str (self )} not found in SonarQube" )
144- for br in data .get ("branches" , []):
145- if br ["name" ] == self .name :
146- self ._load (br )
147- else :
148- # While we're there let's load other branches with up to date branch data
149- Branch .load (self .concerned_object , br ["name" ], data )
135+ data = json .loads (self .get (Branch .API [c .LIST ], params = self .api_params (c .LIST )).text )
136+ br_data = next ((br for br in data .get ("branches" , []) if br ["name" ] == self .name ), None )
137+ if not br_data :
138+ Branch .CACHE .clear ()
139+ raise exceptions .ObjectNotFound (self .name , f"{ str (self )} not found" )
140+ self ._load (br_data )
141+ # While we're there let's load other branches with up to date branch data
142+ for br in [b for b in data .get ("branches" , []) if b ["name" ] != self .name ]:
143+ Branch .load (self .concerned_object , br ["name" ], data )
150144 return self
151145
152146 def _load (self , data : types .ApiPayload ) -> None :
153- if self .sq_json is None :
154- self .sq_json = data
155- else :
156- self .sq_json .update (data )
147+ log .debug ("Loading %s with data %s" , self , data )
148+ self .sq_json = (self .sq_json or {}) | data
157149 self ._is_main = self .sq_json ["isMain" ]
158150 self ._last_analysis = util .string_to_date (self .sq_json .get ("analysisDate" , None ))
159151 self ._keep_when_inactive = self .sq_json .get ("excludedFromPurge" , False )
160152 self ._is_main = self .sq_json .get ("isMain" , False )
161153
162154 def is_kept_when_inactive (self ) -> bool :
163- """
164- :return: Whether the branch is kept when inactive
165- :rtype: bool
166- """
155+ """Returns whether the branch is kept when inactive"""
167156 if self ._keep_when_inactive is None or self .sq_json is None :
168157 self .refresh ()
169158 return self ._keep_when_inactive
170159
171160 def is_main (self ) -> bool :
172- """
173- :return: Whether the branch is the project main branch
174- :rtype: bool
175- """
161+ """Returns whether the branch is the project main branch"""
176162 if self ._is_main is None or self .sq_json is None :
177163 self .refresh ()
178164 return self ._is_main
@@ -186,11 +172,32 @@ def delete(self) -> bool:
186172 """
187173 try :
188174 return super ().delete ()
189- except (ConnectionError , RequestException ) as e :
190- if isinstance (e , HTTPError ) and e .response .status_code == HTTPStatus .BAD_REQUEST :
191- log .warning ("Can't delete %s, it's the main branch" , str (self ))
175+ except exceptions .SonarException as e :
176+ log .warning (e .message )
192177 return False
193178
179+ def get (
180+ self , api : str , params : types .ApiParams = None , data : Optional [str ] = None , mute : tuple [HTTPStatus ] = (), ** kwargs : str
181+ ) -> requests .Response :
182+ """Performs an HTTP GET request for the object"""
183+ try :
184+ return super ().get (api = api , params = params , data = data , mute = mute , ** kwargs )
185+ except exceptions .ObjectNotFound as e :
186+ if re .match (r"Project .+ not found" , e .message ):
187+ log .warning ("Clearing project cache" )
188+ projects .Project .CACHE .clear ()
189+ raise
190+
191+ def post (self , api : str , params : types .ApiParams = None , mute : tuple [HTTPStatus ] = (), ** kwargs : str ) -> requests .Response :
192+ """Performs an HTTP POST request for the object"""
193+ try :
194+ return super ().post (api = api , params = params , mute = mute , ** kwargs )
195+ except exceptions .ObjectNotFound as e :
196+ if re .match (r"Project .+ not found" , e .message ):
197+ log .warning ("Clearing project cache" )
198+ projects .Project .CACHE .clear ()
199+ raise
200+
194201 def new_code (self ) -> str :
195202 """
196203 :return: The branch new code period definition
@@ -199,13 +206,7 @@ def new_code(self) -> str:
199206 if self ._new_code is None and self .endpoint .is_sonarcloud ():
200207 self ._new_code = settings .new_code_to_string ({"inherited" : True })
201208 elif self ._new_code is None :
202- try :
203- data = json .loads (self .get (api = Branch .API ["get_new_code" ], params = self .api_params (c .LIST )).text )
204- except (ConnectionError , RequestException ) as e :
205- util .handle_error (e , f"getting new code period of { str (self )} " , catch_http_statuses = (HTTPStatus .NOT_FOUND ,))
206- Branch .CACHE .pop (self )
207- raise exceptions .ObjectNotFound (self .concerned_object .key , f"{ str (self .concerned_object )} not found" )
208-
209+ data = json .loads (self .get (api = Branch .API ["get_new_code" ], params = self .api_params (c .LIST )).text )
209210 for b in data ["newCodePeriods" ]:
210211 new_code = settings .new_code_to_string (b )
211212 if b ["branchKey" ] == self .name :
@@ -245,24 +246,17 @@ def set_keep_when_inactive(self, keep: bool) -> bool:
245246 :return: Whether the operation was successful
246247 """
247248 log .info ("Setting %s keep when inactive to %s" , self , keep )
248- try :
249- self . post ( "project_branches/set_automatic_deletion_protection" , params = self . api_params () | { "value" : str ( keep ). lower ()})
249+ ok = self . post ( "project_branches/set_automatic_deletion_protection" , params = self . api_params () | { "value" : str ( keep ). lower ()}). ok
250+ if ok :
250251 self ._keep_when_inactive = keep
251- except (ConnectionError , RequestException ) as e :
252- util .handle_error (e , f"setting { str (self )} keep when inactive to { keep } " , catch_all = True )
253- return False
254252 return True
255253
256254 def set_as_main (self ) -> bool :
257255 """Sets the branch as the main branch of the project
258256
259257 :return: Whether the operation was successful
260258 """
261- try :
262- self .post ("api/project_branches/set_main" , params = self .api_params ())
263- except (ConnectionError , RequestException ) as e :
264- util .handle_error (e , f"setting { str (self )} as main branch" , catch_all = True )
265- return False
259+ self .post ("api/project_branches/set_main" , params = self .api_params ())
266260 for b in self .concerned_object .branches ().values ():
267261 b ._is_main = b .name == self .name
268262 return True
@@ -317,30 +311,21 @@ def rename(self, new_name: str) -> bool:
317311 log .debug ("Skipping rename %s with same new name" , str (self ))
318312 return False
319313 log .info ("Renaming main branch of %s from '%s' to '%s'" , str (self .concerned_object ), self .name , new_name )
320- try :
321- self .post (Branch .API [c .RENAME ], params = {"project" : self .concerned_object .key , "name" : new_name })
322- except (ConnectionError , RequestException ) as e :
323- util .handle_error (e , f"Renaming { str (self )} " , catch_http_statuses = (HTTPStatus .NOT_FOUND , HTTPStatus .BAD_REQUEST ))
324- if isinstance (e , HTTPError ):
325- if e .response .status_code == HTTPStatus .NOT_FOUND :
326- Branch .CACHE .pop (self )
327- raise exceptions .ObjectNotFound (self .concerned_object .key , f"str{ self .concerned_object } not found" )
328- if e .response .status_code == HTTPStatus .BAD_REQUEST :
329- return False
314+ self .post (Branch .API [c .RENAME ], params = {"project" : self .concerned_object .key , "name" : new_name })
330315 Branch .CACHE .pop (self )
331316 self .name = new_name
332317 Branch .CACHE .put (self )
333318 return True
334319
335- def get_findings (self ) -> dict [str , object ]:
320+ def get_findings (self , filters : Optional [ types . ApiParams ] = None ) -> dict [str , object ]:
336321 """Returns a branch list of findings
337322
338323 :return: dict of Findings, with finding key as key
339324 :rtype: dict{key: Finding}
340325 """
341- findings = self . get_issues ()
342- findings . update ( self .get_hotspots () )
343- return findings
326+ if not filters :
327+ return self . concerned_object . get_findings ( branch = self .name )
328+ return self . get_issues ( filters ) | self . get_hotspots ( filters )
344329
345330 def component_data (self ) -> dict [str , str ]:
346331 """Returns key data"""
0 commit comments