1414from .exceptions import CloudFlareError , CloudFlareAPIError , CloudFlareInternalError
1515
1616BASE_URL = 'https://api.cloudflare.com/client/v4'
17+ OPENAPI_URL = 'https://github.com/cloudflare/api-schemas/raw/main/openapi.json'
1718
1819DEFAULT_GLOBAL_REQUEST_TIMEOUT = 5
1920DEFAULT_MAX_REQUEST_RETRIES = 5
@@ -38,6 +39,9 @@ def __init__(self, config):
3839 # We must have a base_url value
3940 self .base_url = config ['base_url' ] if 'base_url' in config else BASE_URL
4041
42+ # The modern-day API definition comes from here (soon)
43+ self .openapi_url = config ['openapi_url' ] if 'openapi_url' in config else OPENAPI_URL
44+
4145 self .raw = config ['raw' ]
4246 self .use_sessions = config ['use_sessions' ]
4347 self .global_request_timeout = config ['global_request_timeout' ] if 'global_request_timeout' in config else DEFAULT_GLOBAL_REQUEST_TIMEOUT
@@ -119,12 +123,18 @@ def _add_auth_headers(self, method):
119123 api_token = self .config [v ] if v in self .config else self .api_token
120124
121125 if api_email is None and api_key is None and api_token is None :
126+ if self .logger :
127+ self .logger .debug ('neither email/key or token defined' )
122128 raise CloudFlareAPIError (0 , 'neither email/key or token defined' )
123129
124130 if api_key is not None and api_token is not None :
131+ if self .logger :
132+ self .logger .debug ('confused info - both key and token defined' )
125133 raise CloudFlareAPIError (0 , 'confused info - both key and token defined' )
126134
127135 if api_email is not None and api_key is None and api_token is None :
136+ if self .logger :
137+ self .logger .debug ('email defined however neither key or token defined' )
128138 raise CloudFlareAPIError (0 , 'email defined however neither key or token defined' )
129139
130140 # We know at this point that at-least one api_* is set and no confusion!
@@ -156,14 +166,18 @@ def _add_certtoken_headers(self, method):
156166 api_certtoken = self .api_certtoken # use generic value for all methods
157167
158168 if api_certtoken is None :
169+ if self .logger :
170+ self .logger .debug ('no cert token defined' )
159171 raise CloudFlareAPIError (0 , 'no cert token defined' )
160172 self .headers ['X-Auth-User-Service-Key' ] = api_certtoken
161173
162174 def do_not_available (self , method , parts , identifiers , params = None , data = None , content_type = None , files = None ):
163175 """ Cloudflare v4 API"""
164176
165177 # base class simply returns not available - no processing of any arguments
166- raise CloudFlareAPIError (0 , 'call not available' )
178+ if self .logger :
179+ self .logger .debug ('call for this method not available' )
180+ raise CloudFlareAPIError (0 , 'call for this method not available' )
167181
168182 def do_no_auth (self , method , parts , identifiers , params = None , data = None , content_type = None , files = None ):
169183 """ Cloudflare v4 API"""
@@ -305,8 +319,24 @@ def _call_network(self, method, headers, parts, identifiers, params, data_str, d
305319 else :
306320 self .logger .debug ('Response: %d, %s, %s' , response_code , response_type , '...' )
307321
308- if response_code == 500 :
322+ if response_code == 429 :
323+ # 429 Too Many Requests
324+ # The HTTP 429 Too Many Requests response status code indicates the user
325+ # has sent too many requests in a given amount of time ("rate limiting").
326+ # A Retry-After header might be included to this response indicating how
327+ # long to wait before making a new request.
328+ try :
329+ retry_after = response .headers ['Retry-After' ]
330+ except (KeyError ,IndexError ):
331+ retry_after = ''
332+ # XXX/TODO no processing for now - but could try again within library
333+ if self .logger :
334+ self .logger .debug ('Response: 429 Header Retry-After: %s' , retry_after )
335+
336+ # if (response_code >= 400 and response_code <= 499) or response_code == 500:
337+ if response_code in [400 ,500 ]:
309338 # The /certificates API call insists on a 500 error return and yet has valid error data
339+ # Other API calls can return 400 or 4xx with valid response data
310340 # lets check and convert if able
311341 try :
312342 j = json .loads (response_data )
@@ -317,7 +347,7 @@ def _call_network(self, method, headers, parts, identifiers, params, data_str, d
317347 # yippe - try to continue by allowing to process fully
318348 response_code = 200
319349 except :
320- # ignore - maybe a real 500 !
350+ # ignore - maybe a real error, let proceed !
321351 pass
322352
323353 if response_code >= 500 and response_code <= 599 :
@@ -571,9 +601,11 @@ def _call_unwrapped(self, method, parts, identifiers, params, data_str, data_jso
571601 result = response_data
572602 return result
573603
574- def api_from_openapi (self , url ):
604+ def api_from_openapi (self , url = None ):
575605 """ Cloudflare v4 API"""
576606
607+ if url is None :
608+ url = self .openapi_url
577609 return self ._read_from_web (url )
578610
579611 def _read_from_web (self , url ):
@@ -825,7 +857,7 @@ def _api_list(self, m=None, s=''):
825857 #
826858 # return api_decode_from_web(self._base.api_from_web())
827859
828- def api_from_openapi (self , url ):
860+ def api_from_openapi (self , url = None ):
829861 """ Cloudflare v4 API"""
830862
831863 return api_decode_from_openapi (self ._base .api_from_openapi (url ))
@@ -835,6 +867,15 @@ def __init__(self, email=None, key=None, token=None, certtoken=None, debug=False
835867
836868 self ._base = None
837869
870+ if email and not isinstance (email , str ):
871+ raise TypeError ('email must be str' )
872+ if key and not isinstance (key , str ):
873+ raise TypeError ('key must be str' )
874+ if token and not isinstance (token , str ):
875+ raise TypeError ('token must be str' )
876+ if certtoken and not isinstance (certtoken , str ):
877+ raise TypeError ('certtoken must be str' )
878+
838879 try :
839880 config = read_configs (profile )
840881 except Exception as e :
0 commit comments