33from .config import set_base_url , get_base_url
44import dymoapi .response_models as response_models
55from .services .autoupload import check_for_updates
6+ from .resilience import ResilienceManager , FallbackDataGenerator
7+ from requests import Session
68
79logging .basicConfig (level = logging .ERROR , format = '%(asctime)s - %(levelname)s - %(message)s' )
810
911class DymoAPI :
1012 def __init__ (self , config = {}):
1113 """
12- This is the main class to interact with the Dymo API. It should be
13- instantiated with the root API key and the API key. The root API key is
14- used to fetch the tokens and the API key is used to authenticate the
14+ This is the main class to interact with Dymo API. It should be
15+ instantiated with root API key and API key. The root API key is
16+ used to fetch the tokens and the API key is used to authenticate
1517 requests.
1618
1719 Args:
1820 - options (dict, optional): Options to create the DymoAPI instance.
1921 - options["root_api_key"] (str, optional): The root API key. Defaults to None.
2022 - options["api_key"] (str, optional): The API key. Defaults to None.
2123 - options["base_url"] (str, optional): Whether to use a local server instead of
22- the cloud server. Defaults to False.
24+ cloud server. Defaults to False.
2325 - options["server_email_config"] (dict, optional):
2426 The server email config. Defaults to None.
2527 - options["rules"] (dict, optional): The rules config. Defaults to None.
28+ - options["resilience"] (dict, optional): The resilience config. Defaults to None.
2629
2730 Example:
2831 dymo_api = DymoAPI({
@@ -44,6 +47,29 @@ def __init__(self, config={}):
4447
4548 set_base_url (self .base_url )
4649 self .base_url = get_base_url ()
50+
51+ # Initialize resilience system
52+ resilience = config .get ("resilience" , {})
53+ client_id = self .api_key or self .root_api_key or "anonymous"
54+ self .resilience = ResilienceManager (client_id = client_id )
55+ if resilience :
56+ self .resilience .config .fallback_enabled = resilience .get ("fallback_enabled" , False )
57+ self .resilience .config .retry_attempts = resilience .get ("retry_attempts" , 2 )
58+ self .resilience .config .retry_delay = resilience .get ("retry_delay" , 1000 )
59+
60+ # Initialize requests session
61+ self .session = Session ()
62+ self .session .headers .update ({
63+ "User-Agent" : "DymoAPISDK/1.0.0" ,
64+ "X-Dymo-SDK-Env" : "Python" ,
65+ "X-Dymo-SDK-Version" : "0.0.62"
66+ })
67+
68+ if self .root_api_key or self .api_key :
69+ self .session .headers .update ({
70+ "Authorization" : f"Bearer { self .root_api_key or self .api_key } "
71+ })
72+
4773 check_for_updates ()
4874
4975 def _get_function (self , module_name , function_name = "main" ):
@@ -54,9 +80,9 @@ def _get_function(self, module_name, function_name="main"):
5480
5581 def is_valid_data (self , data : response_models .Validator ) -> response_models .DataVerifierResponse :
5682 """
57- Validates the given data against the configured validation settings.
83+ Validates given data against the configured validation settings.
5884
59- This method requires either the root API key or the API key to be set.
85+ This method requires either root API key or the API key to be set.
6086 If neither is set, it will throw an error.
6187
6288 Args:
@@ -79,31 +105,46 @@ def is_valid_data(self, data: response_models.Validator) -> response_models.Data
79105
80106 [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/data-verifier)
81107 """
82- response = self ._get_function ("private" , "is_valid_data" )(data )
83- if response .get ("ip" ,{}).get ("as" ):
84- response ["ip" ]["_as" ] = response ["ip" ]["as" ]
85- response ["ip" ]["_class" ] = response ["ip" ]["class" ]
86- response ["ip" ].pop ("as" )
87- response ["ip" ].pop ("class" )
88- return response_models .DataVerifierResponse (** response )
108+ fallback_data = FallbackDataGenerator .generate_fallback_data ("isValidData" , data .dict ())
109+
110+ try :
111+ response = self .resilience .execute_with_resilience (
112+ session = self .session ,
113+ method = "POST" ,
114+ url = f"{ self .base_url } /v1/private/secure/verify" ,
115+ json = data .dict () if hasattr (data , 'dict' ) else data ,
116+ fallback_data = fallback_data if self .resilience .config .fallback_enabled else None
117+ )
118+
119+ if response .get ("ip" ,{}).get ("as" ):
120+ response ["ip" ]["_as" ] = response ["ip" ]["as" ]
121+ response ["ip" ]["_class" ] = response ["ip" ]["class" ]
122+ response ["ip" ].pop ("as" )
123+ response ["ip" ].pop ("class" )
124+
125+ return response_models .DataVerifierResponse (** response )
126+ except Exception as e :
127+ if self .resilience .config .fallback_enabled :
128+ return response_models .DataVerifierResponse (** fallback_data )
129+ raise e
89130
90131 def is_valid_email (self , email : str , rules : dict | None = None ) -> bool :
91132 """
92- Wrapper for the private email validation function.
133+ Wrapper for private email validation function.
93134
94- Calls the internal `is_valid_email` function with the provided email and deny rules,
95- returning True or False according to the validation result.
135+ Calls internal `is_valid_email` function with provided email and deny rules,
136+ returning True or False according to validation result.
96137
97138 Args:
98139 email (str): The email address to validate.
99140 rules (dict, optional): Validation rules object with key "deny" (list of deny rules).
100141 ⚠️ Some deny rules are PREMIUM: "NO_MX_RECORDS", "HIGH_RISK_SCORE", "NO_REACHABLE".
101142
102143 Returns:
103- bool: True if the email passes validation, False otherwise.
144+ bool: True if email passes validation, False otherwise.
104145
105146 Raises:
106- APIError: If the underlying validation function fails or the API key is missing.
147+ APIError: If underlying validation function fails or API key is missing.
107148
108149 Example:
109150 >>> valid = dymoClient.is_valid_email(
@@ -115,13 +156,20 @@ def is_valid_email(self, email: str, rules: dict | None = None) -> bool:
115156 https://docs.tpeoficial.com/docs/dymo-api/private/email-validation
116157 """
117158 rules_to_use = rules or self .rules .get ("email" )
118- return self ._get_function ("private" , "is_valid_email" )(email , rules_to_use )
159+ fallback_data = FallbackDataGenerator .generate_fallback_data ("isValidEmail" , email )
160+
161+ try :
162+ return self ._get_function ("private" , "is_valid_email" )(email , rules_to_use )
163+ except Exception as e :
164+ if self .resilience .config .fallback_enabled :
165+ return fallback_data .get ("allow" , False )
166+ raise e
119167
120168 def is_valid_ip (self , ip : str , rules : dict | None = None ) -> bool :
121169 """
122- Wrapper for the private IP validation function.
170+ Wrapper for private IP validation function.
123171
124- Calls the internal `is_valid_ip` function with the provided IP and deny rules,
172+ Calls internal `is_valid_ip` function with the provided IP and deny rules,
125173 returning True or False according to the validation result.
126174
127175 Args:
@@ -130,10 +178,10 @@ def is_valid_ip(self, ip: str, rules: dict | None = None) -> bool:
130178 ⚠️ Some deny rules are PREMIUM: "TOR_NETWORK", "HIGH_RISK_SCORE".
131179
132180 Returns:
133- bool: True if the IP passes validation, False otherwise.
181+ bool: True if IP passes validation, False otherwise.
134182
135183 Raises:
136- APIError: If the underlying validation function fails or the API key is missing.
184+ APIError: If the underlying validation function fails or API key is missing.
137185
138186 Example:
139187 >>> valid = dymoClient.is_valid_ip(
@@ -145,13 +193,20 @@ def is_valid_ip(self, ip: str, rules: dict | None = None) -> bool:
145193 https://docs.tpeoficial.com/docs/dymo-api/private/ip-validation
146194 """
147195 rules_to_use = rules or self .rules .get ("ip" )
148- return self ._get_function ("private" , "is_valid_ip" )(ip , rules_to_use )
196+ fallback_data = FallbackDataGenerator .generate_fallback_data ("isValidIP" , ip )
197+
198+ try :
199+ return self ._get_function ("private" , "is_valid_ip" )(ip , rules_to_use )
200+ except Exception as e :
201+ if self .resilience .config .fallback_enabled :
202+ return fallback_data .get ("allow" , False )
203+ raise e
149204
150205 def is_valid_phone (self , phone : str , rules : dict | None = None ) -> bool :
151206 """
152- Wrapper for the private phone validation function.
207+ Wrapper for private phone validation function.
153208
154- Calls the internal `is_valid_phone` function with the provided phone and deny rules,
209+ Calls internal `is_valid_phone` function with the provided phone and deny rules,
155210 returning True or False according to the validation result.
156211
157212 Args:
@@ -160,10 +215,10 @@ def is_valid_phone(self, phone: str, rules: dict | None = None) -> bool:
160215 ⚠️ Some deny rules are PREMIUM: "HIGH_RISK_SCORE".
161216
162217 Returns:
163- bool: True if the phone passes validation, False otherwise.
218+ bool: True if phone passes validation, False otherwise.
164219
165220 Raises:
166- APIError: If the underlying validation function fails or the API key is missing.
221+ APIError: If the underlying validation function fails or API key is missing.
167222
168223 Example:
169224 >>> valid = dymoClient.is_valid_phone(
@@ -175,8 +230,15 @@ def is_valid_phone(self, phone: str, rules: dict | None = None) -> bool:
175230 https://docs.tpeoficial.com/docs/dymo-api/private/phone-validation
176231 """
177232 rules_to_use = rules or self .rules .get ("phone" )
178- return self ._get_function ("private" , "is_valid_phone" )(phone , rules_to_use )
179-
233+ fallback_data = FallbackDataGenerator .generate_fallback_data ("isValidPhone" , phone )
234+
235+ try :
236+ return self ._get_function ("private" , "is_valid_phone" )(phone , rules_to_use )
237+ except Exception as e :
238+ if self .resilience .config .fallback_enabled :
239+ return fallback_data .get ("allow" , False )
240+ raise e
241+
180242 def send_email (self , data : response_models .EmailStatus ) -> response_models .SendEmailResponse :
181243 """
182244 Sends an email using the configured email client settings.
@@ -190,7 +252,7 @@ def send_email(self, data: response_models.EmailStatus) -> response_models.SendE
190252 - data["to"] (str): The email address to which the email will be sent.
191253 - data["subject"] (str): The subject of the email.
192254 - data["html"] (str, optional): The HTML content of the email.
193- - data["react"] (Object, optional): The React component to be rendered as the email content.
255+ - data["react"] (Object, optional): The React component to be rendered as email content.
194256 - data["options"] (dict, optional): Content configuration options.
195257 - data["options"]["priority"] (str, optional): Email priority (default: "normal"). Allowed values: "high", "normal", "low".
196258 - data["options"]["waitToResponse"] (bool, optional): Wait until the email is sent (default: True).
@@ -223,22 +285,22 @@ def get_random(self, data: response_models.SRNG) -> response_models.SRNGResponse
223285 - data (response_models.SRNG): The data for random number generation.
224286 - data["min"] (int/float): The minimum value of the range.
225287 - data["max"] (int/float): The maximum value of the range.
226- - data["quantity"] (int, optional): The number of random values to generate. Defaults to 1 .
288+ - data["quantity"] (int, optional): The number of random values to generate. Defaults to1 .
227289
228290 Returns:
229291 Promise[response_models.SRNGResponse]: A promise that resolves to the response from the server.
230292
231293 Raises:
232294 Exception: An error will be thrown if there is an issue with the random number
233295 generation process.
234-
296+
235297 [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/secure-random-number-generator)
236298 """
237299 return response_models .DataVerifierResponse (** self ._get_function ("private" , "get_random" )({** data }))
238300
239301 def extract_with_textly (self , data : response_models .Textly ) -> response_models .TextlyResponse :
240302 """
241- Extracts structured data from a given text using the Textly endpoint.
303+ Extracts structured data from a given text using Textly endpoint.
242304
243305 This method requires a valid private API token to authenticate the request.
244306 The input must include both the text to process and a format schema describing
@@ -310,7 +372,7 @@ def is_valid_pwd(self, data: response_models.IsValidPwdData) -> response_models.
310372 """
311373 Validates a password based on the given parameters.
312374
313- This method requires the password to be provided in the data object.
375+ This method requires a password to be provided in the data object.
314376 If the password is not provided, an error will be thrown. The method
315377 will validate the password against the following rules:
316378 - The password must be at least `data["min"]` characters long (default 8).
@@ -323,8 +385,8 @@ def is_valid_pwd(self, data: response_models.IsValidPwdData) -> response_models.
323385
324386 Args:
325387 - data (response_models.IsValidPwdData): The data for password validation.
326- - data["min"] (int, optional): Minimum length of the password. Defaults to 8.
327- - data["max"] (int, optional): Maximum length of the password. Defaults to 32.
388+ - data["min"] (int, optional): Minimum length of password. Defaults to 8.
389+ - data["max"] (int, optional): Maximum length of password. Defaults to 32.
328390 - data["email"] (str, optional): Optional email associated with the password.
329391 - data["password"] (str): The password to be validated.
330392 - data["bannedWords"] (str or list[str], optional): The list of banned words that the password must not contain.
0 commit comments