1919
2020from typing import List
2121
22- from .common import ClientError , LoginError , WorkspaceRole , ProjectRole , LOG_FILE_SIZE_TO_SEND , MERGIN_DEFAULT_LOGS_URL
22+ from .common import (
23+ ClientError ,
24+ LoginError ,
25+ WorkspaceRole ,
26+ ProjectRole ,
27+ MAX_LOG_FILE_SIZE_TO_SEND ,
28+ MERGIN_DEFAULT_LOGS_URL ,
29+ )
2330from .merginproject import MerginProject
2431from .client_pull import (
2532 download_file_finalize ,
@@ -94,7 +101,7 @@ def __init__(
94101 proxy_config = None ,
95102 ):
96103 self .url = url if url is not None else MerginClient .default_url ()
97- self ._auth_params = None
104+ self ._auth_params = {}
98105 self ._auth_session = None
99106 self ._user_info = None
100107 self ._server_type = None
@@ -192,36 +199,32 @@ def user_agent_info(self):
192199 system_version = platform .mac_ver ()[0 ]
193200 return f"{ self .client_version } ({ platform .system ()} /{ system_version } )"
194201
195- def _check_token (f ):
196- """Wrapper for creating/renewing authorization token."""
197-
198- def wrapper (self , * args ):
199- if self ._auth_params :
200- if self ._auth_session :
201- # Refresh auth token if it expired or will expire very soon
202- delta = self ._auth_session ["expire" ] - datetime .now (timezone .utc )
203- if delta .total_seconds () < 5 :
204- self .log .info ("Token has expired - refreshing..." )
205- if self ._auth_params .get ("login" , None ) and self ._auth_params .get ("password" , None ):
206- self .log .info ("Token has expired - refreshing..." )
207- self .login (self ._auth_params ["login" ], self ._auth_params ["password" ])
208- else :
209- raise AuthTokenExpiredError ("Token has expired - please re-login" )
210- else :
211- # Create a new authorization token
212- self .log .info (f"No token - login user: { self ._auth_params ['login' ]} " )
213- if self ._auth_params .get ("login" , None ) and self ._auth_params .get ("password" , None ):
214- self .login (self ._auth_params ["login" ], self ._auth_params ["password" ])
215- else :
216- raise ClientError ("Missing login or password" )
217-
218- return f (self , * args )
202+ def validate_auth (self ):
203+ """Validate that client has valid auth token or can be logged in."""
219204
220- return wrapper
205+ if self ._auth_session :
206+ # Refresh auth token if it expired or will expire very soon
207+ delta = self ._auth_session ["expire" ] - datetime .now (timezone .utc )
208+ if delta .total_seconds () < 5 :
209+ self .log .info ("Token has expired - refreshing..." )
210+ if self ._auth_params .get ("login" , None ) and self ._auth_params .get ("password" , None ):
211+ self .log .info ("Token has expired - refreshing..." )
212+ self .login (self ._auth_params ["login" ], self ._auth_params ["password" ])
213+ else :
214+ raise AuthTokenExpiredError ("Token has expired - please re-login" )
215+ else :
216+ # Create a new authorization token
217+ self .log .info (f"No token - login user: { self ._auth_params .get ('login' , None )} " )
218+ if self ._auth_params .get ("login" , None ) and self ._auth_params .get ("password" , None ):
219+ self .login (self ._auth_params ["login" ], self ._auth_params ["password" ])
220+ else :
221+ raise ClientError ("Missing login or password" )
221222
222- @_check_token
223- def _do_request (self , request ):
223+ def _do_request (self , request , validate_auth = True ):
224224 """General server request method."""
225+ if validate_auth :
226+ self .validate_auth ()
227+
225228 if self ._auth_session :
226229 request .add_header ("Authorization" , self ._auth_session ["token" ])
227230 request .add_header ("User-Agent" , self .user_agent_info ())
@@ -263,31 +266,31 @@ def _do_request(self, request):
263266 # e.g. when DNS resolution fails (no internet connection?)
264267 raise ClientError ("Error requesting " + request .full_url + ": " + str (e ))
265268
266- def get (self , path , data = None , headers = {}):
269+ def get (self , path , data = None , headers = {}, validate_auth = True ):
267270 url = urllib .parse .urljoin (self .url , urllib .parse .quote (path ))
268271 if data :
269272 url += "?" + urllib .parse .urlencode (data )
270273 request = urllib .request .Request (url , headers = headers )
271- return self ._do_request (request )
274+ return self ._do_request (request , validate_auth = validate_auth )
272275
273- def post (self , path , data = None , headers = {}):
276+ def post (self , path , data = None , headers = {}, validate_auth = True ):
274277 url = urllib .parse .urljoin (self .url , urllib .parse .quote (path ))
275278 if headers .get ("Content-Type" , None ) == "application/json" :
276279 data = json .dumps (data , cls = DateTimeEncoder ).encode ("utf-8" )
277280 request = urllib .request .Request (url , data , headers , method = "POST" )
278- return self ._do_request (request )
281+ return self ._do_request (request , validate_auth = validate_auth )
279282
280- def patch (self , path , data = None , headers = {}):
283+ def patch (self , path , data = None , headers = {}, validate_auth = True ):
281284 url = urllib .parse .urljoin (self .url , urllib .parse .quote (path ))
282285 if headers .get ("Content-Type" , None ) == "application/json" :
283286 data = json .dumps (data , cls = DateTimeEncoder ).encode ("utf-8" )
284287 request = urllib .request .Request (url , data , headers , method = "PATCH" )
285- return self ._do_request (request )
288+ return self ._do_request (request , validate_auth = validate_auth )
286289
287- def delete (self , path ):
290+ def delete (self , path , validate_auth = True ):
288291 url = urllib .parse .urljoin (self .url , urllib .parse .quote (path ))
289292 request = urllib .request .Request (url , method = "DELETE" )
290- return self ._do_request (request )
293+ return self ._do_request (request , validate_auth = validate_auth )
291294
292295 def login (self , login , password ):
293296 """
@@ -303,26 +306,16 @@ def login(self, login, password):
303306 self ._auth_session = None
304307 self .log .info (f"Going to log in user { login } " )
305308 try :
306- self ._auth_params = params
307- url = urllib .parse .urljoin (self .url , urllib .parse .quote ("/v1/auth/login" ))
308- data = json .dumps (self ._auth_params , cls = DateTimeEncoder ).encode ("utf-8" )
309- request = urllib .request .Request (url , data , {"Content-Type" : "application/json" }, method = "POST" )
310- request .add_header ("User-Agent" , self .user_agent_info ())
311- resp = self .opener .open (request )
309+ resp = self .post (
310+ "/v1/auth/login" , data = params , headers = {"Content-Type" : "application/json" }, validate_auth = False
311+ )
312312 data = json .load (resp )
313313 session = data ["session" ]
314- except urllib .error .HTTPError as e :
315- if e .headers .get ("Content-Type" , "" ) == "application/problem+json" :
316- info = json .load (e )
317- self .log .info (f"Login problem: { info .get ('detail' )} " )
318- raise LoginError (info .get ("detail" ))
319- self .log .info (f"Login problem: { e .read ().decode ('utf-8' )} " )
320- raise LoginError (e .read ().decode ("utf-8" ))
321- except urllib .error .URLError as e :
322- # e.g. when DNS resolution fails (no internet connection?)
323- raise ClientError ("failure reason: " + str (e .reason ))
314+ except ClientError as e :
315+ self .log .info (f"Login problem: { e .detail } " )
316+ raise LoginError (e .detail )
324317 self ._auth_session = {
325- "token" : "Bearer %s" % session [" token" ] ,
318+ "token" : f "Bearer { session [' token' ] } " ,
326319 "expire" : dateutil .parser .parse (session ["expire" ]),
327320 }
328321 self ._user_info = {"username" : data ["username" ]}
@@ -367,7 +360,7 @@ def server_type(self):
367360 """
368361 if not self ._server_type :
369362 try :
370- resp = self .get ("/config" )
363+ resp = self .get ("/config" , validate_auth = False )
371364 config = json .load (resp )
372365 if config ["server_type" ] == "ce" :
373366 self ._server_type = ServerType .CE
@@ -389,7 +382,7 @@ def server_version(self):
389382 """
390383 if self ._server_version is None :
391384 try :
392- resp = self .get ("/config" )
385+ resp = self .get ("/config" , validate_auth = False )
393386 config = json .load (resp )
394387 self ._server_version = config ["version" ]
395388 except (ClientError , KeyError ):
@@ -1403,7 +1396,7 @@ def remove_project_collaborator(self, project_id: str, user_id: int):
14031396
14041397 def server_config (self ) -> dict :
14051398 """Get server configuration as dictionary."""
1406- response = self .get ("/config" )
1399+ response = self .get ("/config" , validate_auth = False )
14071400 return json .load (response )
14081401
14091402 def send_logs (
@@ -1441,16 +1434,20 @@ def send_logs(
14411434 platform .system (), self .url , self .username ()
14421435 )
14431436
1437+ # We send more from the local logs
1438+ global_logs_file_size_to_send = MAX_LOG_FILE_SIZE_TO_SEND * 0.2
1439+ local_logs_file_size_to_send = MAX_LOG_FILE_SIZE_TO_SEND * 0.8
1440+
14441441 global_logs = b""
14451442 if global_log_file and os .path .exists (global_log_file ):
14461443 with open (global_log_file , "rb" ) as f :
1447- if os .path .getsize (global_log_file ) > LOG_FILE_SIZE_TO_SEND :
1448- f .seek (- LOG_FILE_SIZE_TO_SEND , os .SEEK_END )
1444+ if os .path .getsize (global_log_file ) > global_logs_file_size_to_send :
1445+ f .seek (- global_logs_file_size_to_send , os .SEEK_END )
14491446 global_logs = f .read () + b"\n --------------------------------\n \n "
14501447
14511448 with open (logfile , "rb" ) as f :
1452- if os .path .getsize (logfile ) > 512 * 1024 :
1453- f .seek (- 512 * 1024 , os .SEEK_END )
1449+ if os .path .getsize (logfile ) > local_logs_file_size_to_send :
1450+ f .seek (- local_logs_file_size_to_send , os .SEEK_END )
14541451 logs = f .read ()
14551452
14561453 payload = meta .encode () + global_logs + logs
0 commit comments