Source code for askpablos_api.validators

"""
askpablos_api.validators

Parameter validation utilities for the AskPablos API client.

This module contains validation logic for request parameters, ensuring
proper parameter combinations and providing clear error messages.
Separating validation logic improves code clarity and reusability.
"""

from typing import Dict, Any, Optional
from .exceptions import ConfigurationError


[docs] class ParameterValidator: """ Validates request parameters and options for API calls. This class provides centralized validation logic to ensure that parameter combinations are valid and provide helpful error messages when invalid combinations are detected. """
[docs] @staticmethod def validate_browser_dependencies( browser: bool, screenshot: bool = False ) -> None: """ Validate that browser-dependent features are only used with browser=True. The following parameters require browser=True to function: - screenshot: Requires browser automation to capture page screenshots Args: browser: Whether browser mode is enabled screenshot: Whether screenshot capture is requested Raises: ConfigurationError: If browser features are requested without browser=True """ if browser: return # All features are valid when browser is enabled if screenshot: raise ConfigurationError( f"CONFIGURATION ERROR: browser=True is required when using: screenshot=True." )
[docs] @staticmethod def validate_method(method: str) -> None: """ Validate that the HTTP method is supported. Args: method: HTTP method to validate Raises: ValueError: If the method is not one of the supported values """ if not isinstance(method, str): raise ValueError("Method must be a string") valid_methods = ['GET', 'POST'] if method.upper() not in valid_methods: raise ValueError( f"Method must be one of {valid_methods}, got '{method}'" )
[docs] @staticmethod def validate_body(method: str, body: Optional[Any]) -> None: """ Validate that a request body is only used with POST requests. Args: method: HTTP method for the request body: Request body to validate Raises: ConfigurationError: If body is provided for a non-POST request """ if body is not None and method.upper() != 'POST': raise ConfigurationError( "CONFIGURATION ERROR: 'body' can only be used when method='POST'." )
[docs] @staticmethod def validate_url(url: str) -> None: """ Validate that the URL is properly formatted. Args: url: URL to validate Raises: ValueError: If URL is invalid """ if not url: raise ValueError("URL is required and cannot be empty") if not isinstance(url, str): raise ValueError("URL must be a string") # Basic URL validation if not (url.startswith('http://') or url.startswith('https://')): raise ValueError("URL must start with 'http://' or 'https://'")
[docs] @staticmethod def validate_timeout(timeout: int) -> None: """ Validate timeout parameter. Args: timeout: Timeout value in seconds Raises: ValueError: If timeout is invalid """ if not isinstance(timeout, int): raise ValueError("Timeout must be an integer") if timeout <= 0: raise ValueError("Timeout must be greater than 0") if timeout > 300: # 5 minutes max raise ValueError("Timeout cannot exceed 300 seconds")
[docs] @staticmethod def validate_headers(headers: Optional[Dict[str, str]]) -> None: """ Validate headers parameter. Args: headers: Headers dictionary to validate Raises: ValueError: If headers are invalid """ if headers is None: return if not isinstance(headers, dict): raise ValueError("Headers must be a dictionary") for key, value in headers.items(): if not isinstance(key, str): raise ValueError(f"Header key must be string, got {type(key)}") if not isinstance(value, str): raise ValueError(f"Header value must be string, got {type(value)}")
[docs] @staticmethod def validate_geo_location(config: Dict[str, Any], validated_config: Dict[str, Any]) -> None: """ Validate geoLocation configuration. Args: config: Raw configuration dictionary validated_config: Dictionary to store validated config Raises: ValueError: If geoLocation value is invalid """ if 'geoLocation' in config: geo = config['geoLocation'] if not isinstance(geo, str): raise ValueError("'geoLocation' must be a string") geo = geo.strip().lower() if len(geo) != 2 or not geo.isalpha(): raise ValueError( f"'geoLocation' must be a 2-letter ISO country code (e.g. 'US', 'PK', 'GB'), got '{config['geoLocation']}'" ) validated_config['geoLocation'] = geo
[docs] @staticmethod def validate_proxy_type(config: Dict[str, Any], validated_config: Dict[str, Any]) -> None: """ Validate proxyType configuration. Args: config: Raw configuration dictionary validated_config: Dictionary to store validated config Raises: ValueError: If proxyType value is invalid """ if 'proxyType' in config: proxy_type = config['proxyType'] valid_proxy_types = ['datacenter', 'residential', 'mobile'] if not isinstance(proxy_type, str): raise ValueError("'proxyType' must be a string") if proxy_type not in valid_proxy_types: raise ValueError( f"'proxyType' must be one of {valid_proxy_types}, got '{proxy_type}'" ) validated_config['proxyType'] = proxy_type
[docs] @classmethod def validate_request_params( cls, url: str, headers: Optional[Dict[str, str]] = None, browser: bool = False, screenshot: bool = False, timeout: int = 30, method: str = "GET", body: Optional[Any] = None, options: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """ Validate all request parameters at once. Args: url: Target URL headers: Custom headers browser: Browser mode flag screenshot: Screenshot capture flag timeout: Request timeout method: HTTP method (GET or POST) body: Request body, only valid when method='POST' options: Additional options (geoLocation, proxyType, etc.) Returns: Dict[str, Any]: Validated and normalized options Raises: ValueError: If any parameter is invalid """ cls.validate_url(url) cls.validate_headers(headers) cls.validate_timeout(timeout) cls.validate_browser_dependencies( browser=browser, screenshot=screenshot ) cls.validate_method(method) cls.validate_body(method, body) validated_config: Dict[str, Any] = {} if options: cls.validate_geo_location(options, validated_config) cls.validate_proxy_type(options, validated_config) return validated_config