11from json import JSONDecodeError
2+ from typing import Dict
23from typing import List
34from typing import Optional
4- from typing import Union
5+ from typing import Type
6+ from typing import TypeVar
57
68import httpx
79from httpx import Request
810from httpx import Response
911
1012from . import models
13+ from .errors import CheckedIDAuthenticationError
14+ from .errors import CheckedIDError
15+ from .errors import CheckedIDNotFoundError
16+ from .errors import CheckedIDValidationError
17+
18+
19+ _T = TypeVar ("_T" )
1120
1221
1322class Client :
23+ ERROR_RESPONSE_MAPPING : Dict [int , Type [CheckedIDError ]] = {
24+ 422 : CheckedIDValidationError ,
25+ 403 : CheckedIDAuthenticationError ,
26+ 404 : CheckedIDNotFoundError ,
27+ }
28+
1429 def __init__ (self , customer_code : str , base_url : str = "https://api.checkedid.eu/" ):
1530 self .httpx = httpx .Client (base_url = base_url , auth = self .authenticate_request )
1631 self .access_token : Optional [str ] = None
@@ -21,39 +36,43 @@ def authenticate_request(self, request: Request) -> Request:
2136 request .headers ["Authorization" ] = f"Bearer { self .access_token } "
2237 return request
2338
39+ def process_response (
40+ self , response : Response , model : Type [_T ], status_code_success : int = 200
41+ ) -> Optional [_T ]:
42+ if response .status_code == status_code_success :
43+ return model (** response .json ())
44+
45+ self .handle_error_response (response )
46+
47+ return None
48+
2449 def oauth_token (
2550 self , grant_type : str , username : str , password : str
26- ) -> Union [models .OAuthToken , models . ErrorResponse ]:
51+ ) -> Optional [models .OAuthToken ]:
2752 response = self .httpx .post (
2853 "/oauth/token" ,
2954 data = {"grant_type" : grant_type , "username" : username , "password" : password },
3055 )
3156
32- if response .status_code == 200 :
33- typed_response = models .OAuthToken (** response .json ())
57+ typed_response = self .process_response (response , models .OAuthToken )
3458
59+ if typed_response :
3560 self .access_token = typed_response .access_token
3661
3762 return typed_response
38- else :
39- return self .handle_error_response (response )
63+ return None
4064
41- def invitation_status (
42- self , invitation_code : str
43- ) -> Union [models .Invitation , models .ErrorResponse ]:
65+ def invitation_status (self , invitation_code : str ) -> Optional [models .Invitation ]:
4466 response : Response = self .httpx .get (
4567 f"/result/status/{ invitation_code } " ,
4668 headers = {"Accept" : "application/json" },
4769 )
4870
49- if response .status_code == 200 :
50- return models .Invitation (** response .json ())
51- else :
52- return self .handle_error_response (response )
71+ return self .process_response (response , models .Invitation )
5372
5473 def invitations_create (
5574 self , invitations : List [models .CreateInvitationRequest ]
56- ) -> Union [models .CustomerDetails , models . ErrorResponse ]:
75+ ) -> Optional [models .CustomerDetails ]:
5776 obj = models .CreateInvitationDetails (
5877 CustomerCode = self .customer_code , Invitations = invitations
5978 )
@@ -64,47 +83,53 @@ def invitations_create(
6483 headers = {"Accept" : "application/json" , "Content-Type" : "application/json" },
6584 )
6685
67- if response .status_code == 200 :
68- return models .CustomerDetails (** response .json ())
69- else :
70- return self .handle_error_response (response )
86+ return self .process_response (response , models .CustomerDetails )
7187
72- def invitation_delete (
73- self , invitation_code : str
74- ) -> Union [models .ErrorResponse , bool ]:
88+ def invitation_delete (self , invitation_code : str ) -> bool :
7589 response : Response = self .httpx .delete (
7690 f"/invitation/{ self .customer_code } /{ invitation_code } " ,
7791 headers = {"Accept" : "application/json" },
7892 )
7993
8094 if response .status_code == 200 :
8195 return True
82- else :
83- return self .handle_error_response (response )
8496
85- def dossier (
86- self , dossier_number : str
87- ) -> Union [models .ReportResponse , models .ErrorResponse ]:
97+ self .handle_error_response (response )
98+
99+ return False
100+
101+ def dossier (self , dossier_number : str ) -> Optional [models .ReportResponse ]:
88102 response = self .httpx .get (f"/report/{ dossier_number } " )
89103
90- if response .status_code == 200 :
91- return models .ReportResponse (** response .json ())
92- return self .handle_error_response (response )
104+ return self .process_response (response , models .ReportResponse )
93105
94106 def dossier_with_scope (
95107 self , dossier_number : str , scope : str
96- ) -> Union [models .ReportDataV3 , models . ErrorResponse ]:
108+ ) -> Optional [models .ReportDataV3 ]:
97109 response = self .httpx .get (f"/reportdata/{ dossier_number } /{ scope } " )
98110
99- if response .status_code == 200 :
100- return models .ReportDataV3 (** response .json ())
101- return self .handle_error_response (response )
111+ return self .process_response (response , models .ReportDataV3 )
112+
113+ def handle_error_response (self , response : Response ) -> None :
114+ if response .status_code == 400 :
115+ raise CheckedIDValidationError (
116+ response .text , status_code = response .status_code
117+ )
102118
103- def handle_error_response (self , response : Response ) -> models .ErrorResponse :
104119 try :
105120 json = response .json ()
106121 except JSONDecodeError :
107122 json = {"message" : response .text }
108123
109124 json ["status_code" ] = response .status_code
110- return models .ErrorResponse (** json )
125+
126+ exception_type = self .map_exception (response )
127+ raise exception_type (
128+ status_code = response .status_code , json = json , message = "Error from server"
129+ )
130+
131+ def map_exception (self , response : Response ) -> Type [CheckedIDError ]:
132+ exception_type = self .ERROR_RESPONSE_MAPPING .get (
133+ response .status_code , CheckedIDError
134+ )
135+ return exception_type
0 commit comments