88import dacite
99import httpx
1010import tenacity
11- from httpx import TimeoutException
1211from httpx ._types import QueryParamTypes , HeaderTypes
1312
1413from saic_ismart_client_ng .api .schema import LoginResp
1514from saic_ismart_client_ng .crypto_utils import sha1_hex_digest
1615from saic_ismart_client_ng .exceptions import SaicApiException , SaicApiRetryException , SaicLogoutException
1716from saic_ismart_client_ng .listener import SaicApiListener
1817from saic_ismart_client_ng .model import SaicApiConfiguration
19- from saic_ismart_client_ng .net .client .api import SaicApiClient
20- from saic_ismart_client_ng .net .client .login import SaicLoginClient
18+ from saic_ismart_client_ng .net .client import SaicApiClient
2119
2220logger = logging .getLogger (__name__ )
2321
@@ -29,49 +27,36 @@ def __init__(
2927 listener : SaicApiListener = None ,
3028 ):
3129 self .__configuration = configuration
32- self .__login_client = SaicLoginClient (configuration , listener = listener )
3330 self .__api_client = SaicApiClient (configuration , listener = listener )
3431 self .__token_expiration : Optional [datetime .datetime ] = None
3532
36- @property
37- def configuration (self ) -> SaicApiConfiguration :
38- return self .__configuration
39-
40- @property
41- def login_client (self ) -> SaicLoginClient :
42- return self .__login_client
43-
44- @property
45- def api_client (self ) -> SaicApiClient :
46- return self .__api_client
47-
48- @property
49- def token_expiration (self ) -> Optional [datetime .datetime ]:
50- return self .__token_expiration
51-
5233 async def login (self ) -> LoginResp :
53- url = f"{ self .configuration .base_uri } oauth/token"
5434 headers = {
5535 "Content-Type" : "application/x-www-form-urlencoded" ,
5636 "Accept" : "application/json" ,
37+ "Authorization" : "Basic c3dvcmQ6c3dvcmRfc2VjcmV0"
5738 }
5839 firebase_device_id = "cqSHOMG1SmK4k-fzAeK6hr:APA91bGtGihOG5SEQ9hPx3Dtr9o9mQguNiKZrQzboa-1C_UBlRZYdFcMmdfLvh9Q_xA8A0dGFIjkMhZbdIXOYnKfHCeWafAfLXOrxBS3N18T4Slr-x9qpV6FHLMhE9s7I6s89k9lU7DD"
5940 form_body = {
6041 "grant_type" : "password" ,
61- "username" : self .configuration .username ,
62- "password" : sha1_hex_digest (self .configuration .password ),
42+ "username" : self .__configuration .username ,
43+ "password" : sha1_hex_digest (self .__configuration .password ),
6344 "scope" : "all" ,
6445 "deviceId" : f"{ firebase_device_id } ###europecar" ,
6546 "deviceType" : "1" , # 2 for huawei
66- "loginType" : "2" if self .configuration .username_is_email else "1" ,
67- "countryCode" : "" if self .configuration .username_is_email else self .configuration .phone_country_code ,
47+ "loginType" : "2" if self .__configuration .username_is_email else "1" ,
48+ "countryCode" : "" if self .__configuration .username_is_email else self .__configuration .phone_country_code ,
6849 }
6950
70- req = httpx .Request ("POST" , url , data = form_body , headers = headers )
71- response = await self .login_client .client .send (req )
72- result = await self .deserialize (req , response , LoginResp )
51+ result = await self .execute_api_call (
52+ "POST" ,
53+ "/oauth/token" ,
54+ form_body = form_body ,
55+ out_type = LoginResp ,
56+ headers = headers
57+ )
7358 # Update the user token
74- self .api_client .user_token = result .access_token
59+ self .__api_client .user_token = result .access_token
7560 self .__token_expiration = datetime .datetime .now () + datetime .timedelta (seconds = result .expires_in )
7661 return result
7762
@@ -81,15 +66,42 @@ async def execute_api_call(
8166 path : str ,
8267 * ,
8368 body : Optional [Any ] = None ,
69+ form_body : Optional [Any ] = None ,
70+ out_type : Optional [Type [T ]] = None ,
71+ params : Optional [QueryParamTypes ] = None ,
72+ headers : Optional [HeaderTypes ] = None ,
73+ ) -> Optional [T ]:
74+ try :
75+ return await self .__execute_api_call (
76+ method ,
77+ path ,
78+ body = body ,
79+ form_body = form_body ,
80+ out_type = out_type ,
81+ params = params ,
82+ headers = headers
83+ )
84+ except SaicApiException as e :
85+ raise e
86+ except Exception as e :
87+ raise SaicApiException (f"API call { method } { path } failed unexpectedly" , return_code = 500 ) from e
88+
89+ async def __execute_api_call (
90+ self ,
91+ method : str ,
92+ path : str ,
93+ * ,
94+ body : Optional [Any ] = None ,
95+ form_body : Optional [Any ] = None ,
8496 out_type : Optional [Type [T ]] = None ,
8597 params : Optional [QueryParamTypes ] = None ,
8698 headers : Optional [HeaderTypes ] = None ,
8799 ) -> Optional [T ]:
88100 url = f"{ self .__configuration .base_uri } { path [1 :] if path .startswith ('/' ) else path } "
89101 json_body = asdict (body ) if body else None
90- req = httpx .Request (method , url , params = params , headers = headers , json = json_body )
91- response = await self .api_client . client .send (req )
92- return await self .deserialize (req , response , out_type )
102+ req = httpx .Request (method , url , params = params , headers = headers , data = form_body , json = json_body )
103+ response = await self .__api_client .send (req )
104+ return await self .__deserialize (req , response , out_type )
93105
94106 async def execute_api_call_with_event_id (
95107 self ,
@@ -112,7 +124,7 @@ async def execute_api_call_with_event_id(
112124 async def execute_api_call_with_event_id_inner (* , event_id : str ):
113125 actual_headers = headers or dict ()
114126 actual_headers .update ({'event-id' : event_id })
115- return await self .execute_api_call (
127+ return await self .__execute_api_call (
116128 method ,
117129 path ,
118130 body = body ,
@@ -123,7 +135,7 @@ async def execute_api_call_with_event_id_inner(*, event_id: str):
123135
124136 return await execute_api_call_with_event_id_inner (event_id = '0' )
125137
126- async def deserialize (
138+ async def __deserialize (
127139 self ,
128140 request : httpx .Request ,
129141 response : httpx .Response ,
@@ -184,23 +196,35 @@ async def deserialize(
184196 if response .is_error :
185197 if response .status_code in (401 , 403 ):
186198 logger .error (
187- f"API call failed due to an authentication failure: { response .status_code } { response .text } "
199+ f"API call failed due to an authentication failure: { response .status_code } { response .text } " ,
200+ exc_info = e
188201 )
189202 self .logout ()
190- raise SaicLogoutException (response .text , response .status_code )
203+ raise SaicLogoutException (response .text , response .status_code ) from e
191204 else :
192- logger .error (f"API call failed: { response .status_code } { response .text } " )
193- raise SaicApiException (response .text , response .status_code )
205+ logger .error (
206+ f"API call failed: { response .status_code } { response .text } " ,
207+ exc_info = e
208+ )
209+ raise SaicApiException (response .text , response .status_code ) from e
194210 else :
195211 raise SaicApiException (f"Failed to deserialize response: { e } . Original json was { response .text } " ) from e
196212
197213 def logout (self ):
198- self .api_client .user_token = None
214+ self .__api_client .user_token = None
199215 self .__token_expiration = None
200216
217+ @property
201218 def is_logged_in (self ) -> bool :
202- return self .__token_expiration is not None \
203- and self .__token_expiration > datetime .datetime .now ()
219+ return (
220+ self .__api_client .user_token is not None and
221+ self .__token_expiration is not None and
222+ self .__token_expiration > datetime .datetime .now ()
223+ )
224+
225+ @property
226+ def token_expiration (self ) -> Optional [datetime .datetime ]:
227+ return self .__token_expiration
204228
205229
206230def saic_api_after_retry (retry_state ):
0 commit comments