diff --git a/tests/unit/rest/test_client.py b/tests/unit/rest/test_client.py index 210a7d9f9f..63e639ee4d 100644 --- a/tests/unit/rest/test_client.py +++ b/tests/unit/rest/test_client.py @@ -1,53 +1,8 @@ import unittest -import platform -from twilio import __version__ from twilio.rest import ( - Client, - TwilioClient, - TwilioRestClient, - TwilioIpMessagingClient, - TwilioLookupsClient, - TwilioMonitorClient, - TwilioPricingClient, - TwilioTaskRouterClient, - TwilioTrunkingClient, + Client ) -from twilio.base.obsolete import ObsoleteException - - -class TestDummyClients(unittest.TestCase): - def test_obsolete_exception_twilioclient(self): - self.assertRaises(ObsoleteException, TwilioClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twiliorestclient(self): - self.assertRaises(ObsoleteException, TwilioRestClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twilioipmessagingclient(self): - self.assertRaises(ObsoleteException, TwilioIpMessagingClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twiliolookupsclient(self): - self.assertRaises(ObsoleteException, TwilioLookupsClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twiliomonitorclient(self): - self.assertRaises(ObsoleteException, TwilioMonitorClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twiliopricingclient(self): - self.assertRaises(ObsoleteException, TwilioPricingClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twiliotaskrouterclient(self): - self.assertRaises(ObsoleteException, TwilioTaskRouterClient, - "Expected raised ObsoleteException") - - def test_obsolete_exception_twiliotrunkingclient(self): - self.assertRaises(ObsoleteException, TwilioTrunkingClient, - "Expected raised ObsoleteException") class TestRegionEdgeClients(unittest.TestCase): diff --git a/twilio/base/__init__.py b/twilio/base/__init__.py index e69de29bb2..2c7078de1e 100644 --- a/twilio/base/__init__.py +++ b/twilio/base/__init__.py @@ -0,0 +1,154 @@ +import os +import platform +from twilio import __version__ +from twilio.base.exceptions import TwilioException +from twilio.base.obsolete import obsolete_client +from twilio.http.http_client import TwilioHttpClient +from urllib.parse import ( + urlparse, + urlunparse, +) + + +class ClientBase(object): + """ A client for accessing the Twilio API. """ + + def __init__(self, username=None, password=None, account_sid=None, region=None, + http_client=None, environment=None, edge=None, + user_agent_extensions=None): + """ + Initializes the Twilio Client + + :param str username: Username to authenticate with + :param str password: Password to authenticate with + :param str account_sid: Account SID, defaults to Username + :param str region: Twilio Region to make requests to, defaults to 'us1' if an edge is provided + :param HttpClient http_client: HttpClient, defaults to TwilioHttpClient + :param dict environment: Environment to look for auth details, defaults to os.environ + :param str edge: Twilio Edge to make requests to, defaults to None + :param list[str] user_agent_extensions: Additions to the user agent string + + :returns: Twilio Client + :rtype: twilio.rest.Client + """ + environment = environment or os.environ + + self.username = username or environment.get('TWILIO_ACCOUNT_SID') + """ :type : str """ + self.password = password or environment.get('TWILIO_AUTH_TOKEN') + """ :type : str """ + self.account_sid = account_sid or self.username + """ :type : str """ + self.edge = edge or environment.get('TWILIO_EDGE') + """ :type : str """ + self.region = region or environment.get('TWILIO_REGION') + """ :type : str """ + self.user_agent_extensions = user_agent_extensions or [] + """ :type : list[str] """ + + if not self.username or not self.password: + raise TwilioException("Credentials are required to create a TwilioClient") + + self.auth = (self.username, self.password) + """ :type : tuple(str, str) """ + self.http_client = http_client or TwilioHttpClient() + """ :type : HttpClient """ + + def request(self, method, uri, params=None, data=None, headers=None, auth=None, + timeout=None, allow_redirects=False): + """ + Makes a request to the Twilio API using the configured http client + Authentication information is automatically added if none is provided + + :param str method: HTTP Method + :param str uri: Fully qualified url + :param dict[str, str] params: Query string parameters + :param dict[str, str] data: POST body data + :param dict[str, str] headers: HTTP Headers + :param tuple(str, str) auth: Authentication + :param int timeout: Timeout in seconds + :param bool allow_redirects: Should the client follow redirects + + :returns: Response from the Twilio API + :rtype: twilio.http.response.Response + """ + auth = auth or self.auth + headers = headers or {} + + pkg_version = __version__ + os_name = platform.system() + os_arch = platform.machine() + python_version = platform.python_version() + headers['User-Agent'] = 'twilio-python/{} ({} {}) Python/{}'.format( + pkg_version, + os_name, + os_arch, + python_version, + ) + for extension in self.user_agent_extensions: + headers['User-Agent'] += ' {}'.format(extension) + headers['X-Twilio-Client'] = 'python-{}'.format(__version__) + headers['Accept-Charset'] = 'utf-8' + + if method == 'POST' and 'Content-Type' not in headers: + headers['Content-Type'] = 'application/x-www-form-urlencoded' + + if 'Accept' not in headers: + headers['Accept'] = 'application/json' + + uri = self.get_hostname(uri) + + return self.http_client.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects + ) + + def get_hostname(self, uri): + """ + Determines the proper hostname given edge and region preferences + via client configuration or uri. + + :param str uri: Fully qualified url + + :returns: The final uri used to make the request + :rtype: str + """ + if not self.edge and not self.region: + return uri + + parsed_url = urlparse(uri) + pieces = parsed_url.netloc.split('.') + prefix = pieces[0] + suffix = '.'.join(pieces[-2:]) + region = None + edge = None + if len(pieces) == 4: + # product.region.twilio.com + region = pieces[1] + elif len(pieces) == 5: + # product.edge.region.twilio.com + edge = pieces[1] + region = pieces[2] + + edge = self.edge or edge + region = self.region or region or (edge and 'us1') + + parsed_url = parsed_url._replace( + netloc='.'.join([part for part in [prefix, edge, region, suffix] if part]) + ) + return urlunparse(parsed_url) + + def __repr__(self): + """ + Provide a friendly representation + + :returns: Machine friendly representation + :rtype: str + """ + return ''.format(self.account_sid) diff --git a/twilio/rest/__init__.py b/twilio/rest/__init__.py index 91a480e705..c04063a90a 100644 --- a/twilio/rest/__init__.py +++ b/twilio/rest/__init__.py @@ -5,20 +5,10 @@ | (_)\/(_)(_|\/| |(/_ v1.0.0 / / """ +from twilio.base import ClientBase -import os -import platform -from twilio import __version__ -from twilio.base.exceptions import TwilioException -from twilio.base.obsolete import obsolete_client -from twilio.http.http_client import TwilioHttpClient -from urllib.parse import ( - urlparse, - urlunparse, -) - -class Client(object): +class Client(ClientBase): """ A client for accessing the Twilio API. """ def __init__(self, username=None, password=None, account_sid=None, region=None, @@ -39,28 +29,7 @@ def __init__(self, username=None, password=None, account_sid=None, region=None, :returns: Twilio Client :rtype: twilio.rest.Client """ - environment = environment or os.environ - - self.username = username or environment.get('TWILIO_ACCOUNT_SID') - """ :type : str """ - self.password = password or environment.get('TWILIO_AUTH_TOKEN') - """ :type : str """ - self.account_sid = account_sid or self.username - """ :type : str """ - self.edge = edge or environment.get('TWILIO_EDGE') - """ :type : str """ - self.region = region or environment.get('TWILIO_REGION') - """ :type : str """ - self.user_agent_extensions = user_agent_extensions or [] - """ :type : list[str] """ - - if not self.username or not self.password: - raise TwilioException("Credentials are required to create a TwilioClient") - - self.auth = (self.username, self.password) - """ :type : tuple(str, str) """ - self.http_client = http_client or TwilioHttpClient() - """ :type : HttpClient """ + super().__init__(username, password, account_sid, region, http_client, environment, edge, user_agent_extensions) # Domains self._accounts = None @@ -99,96 +68,6 @@ def __init__(self, username=None, password=None, account_sid=None, region=None, self._bulkexports = None self._microvisor = None - def request(self, method, uri, params=None, data=None, headers=None, auth=None, - timeout=None, allow_redirects=False): - """ - Makes a request to the Twilio API using the configured http client - Authentication information is automatically added if none is provided - - :param str method: HTTP Method - :param str uri: Fully qualified url - :param dict[str, str] params: Query string parameters - :param dict[str, str] data: POST body data - :param dict[str, str] headers: HTTP Headers - :param tuple(str, str) auth: Authentication - :param int timeout: Timeout in seconds - :param bool allow_redirects: Should the client follow redirects - - :returns: Response from the Twilio API - :rtype: twilio.http.response.Response - """ - auth = auth or self.auth - headers = headers or {} - - pkg_version = __version__ - os_name = platform.system() - os_arch = platform.machine() - python_version = platform.python_version() - headers['User-Agent'] = 'twilio-python/{} ({} {}) Python/{}'.format( - pkg_version, - os_name, - os_arch, - python_version, - ) - for extension in self.user_agent_extensions: - headers['User-Agent'] += ' {}'.format(extension) - headers['X-Twilio-Client'] = 'python-{}'.format(__version__) - headers['Accept-Charset'] = 'utf-8' - - if method == 'POST' and 'Content-Type' not in headers: - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - if 'Accept' not in headers: - headers['Accept'] = 'application/json' - - uri = self.get_hostname(uri) - - return self.http_client.request( - method, - uri, - params=params, - data=data, - headers=headers, - auth=auth, - timeout=timeout, - allow_redirects=allow_redirects - ) - - def get_hostname(self, uri): - """ - Determines the proper hostname given edge and region preferences - via client configuration or uri. - - :param str uri: Fully qualified url - - :returns: The final uri used to make the request - :rtype: str - """ - if not self.edge and not self.region: - return uri - - parsed_url = urlparse(uri) - pieces = parsed_url.netloc.split('.') - prefix = pieces[0] - suffix = '.'.join(pieces[-2:]) - region = None - edge = None - if len(pieces) == 4: - # product.region.twilio.com - region = pieces[1] - elif len(pieces) == 5: - # product.edge.region.twilio.com - edge = pieces[1] - region = pieces[2] - - edge = self.edge or edge - region = self.region or region or (edge and 'us1') - - parsed_url = parsed_url._replace( - netloc='.'.join([part for part in [prefix, edge, region, suffix] if part]) - ) - return urlunparse(parsed_url) - @property def accounts(self): """ @@ -811,84 +690,3 @@ def validation_requests(self): :rtype: twilio.rest.api.v2010.account.validation_request.ValidationRequestList """ return self.api.account.validation_requests - - def __repr__(self): - """ - Provide a friendly representation - - :returns: Machine friendly representation - :rtype: str - """ - return ''.format(self.account_sid) - - -@obsolete_client -class TwilioClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioRestClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioIpMessagingClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioLookupsClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioMonitorClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioPricingClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioTaskRouterClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass - - -@obsolete_client -class TwilioTrunkingClient(object): - """ Dummy client which provides no functionality. Please use - twilio.rest.Client instead. """ - - def __init__(self, *args): - pass