Skip to content

Commit 1a587ee

Browse files
authored
chore: refactor client (#634)
1 parent d8b3ce1 commit 1a587ee

File tree

3 files changed

+158
-251
lines changed

3 files changed

+158
-251
lines changed

tests/unit/rest/test_client.py

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,8 @@
11
import unittest
2-
import platform
32

4-
from twilio import __version__
53
from twilio.rest import (
6-
Client,
7-
TwilioClient,
8-
TwilioRestClient,
9-
TwilioIpMessagingClient,
10-
TwilioLookupsClient,
11-
TwilioMonitorClient,
12-
TwilioPricingClient,
13-
TwilioTaskRouterClient,
14-
TwilioTrunkingClient,
4+
Client
155
)
16-
from twilio.base.obsolete import ObsoleteException
17-
18-
19-
class TestDummyClients(unittest.TestCase):
20-
def test_obsolete_exception_twilioclient(self):
21-
self.assertRaises(ObsoleteException, TwilioClient,
22-
"Expected raised ObsoleteException")
23-
24-
def test_obsolete_exception_twiliorestclient(self):
25-
self.assertRaises(ObsoleteException, TwilioRestClient,
26-
"Expected raised ObsoleteException")
27-
28-
def test_obsolete_exception_twilioipmessagingclient(self):
29-
self.assertRaises(ObsoleteException, TwilioIpMessagingClient,
30-
"Expected raised ObsoleteException")
31-
32-
def test_obsolete_exception_twiliolookupsclient(self):
33-
self.assertRaises(ObsoleteException, TwilioLookupsClient,
34-
"Expected raised ObsoleteException")
35-
36-
def test_obsolete_exception_twiliomonitorclient(self):
37-
self.assertRaises(ObsoleteException, TwilioMonitorClient,
38-
"Expected raised ObsoleteException")
39-
40-
def test_obsolete_exception_twiliopricingclient(self):
41-
self.assertRaises(ObsoleteException, TwilioPricingClient,
42-
"Expected raised ObsoleteException")
43-
44-
def test_obsolete_exception_twiliotaskrouterclient(self):
45-
self.assertRaises(ObsoleteException, TwilioTaskRouterClient,
46-
"Expected raised ObsoleteException")
47-
48-
def test_obsolete_exception_twiliotrunkingclient(self):
49-
self.assertRaises(ObsoleteException, TwilioTrunkingClient,
50-
"Expected raised ObsoleteException")
516

527

538
class TestRegionEdgeClients(unittest.TestCase):

twilio/base/__init__.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import os
2+
import platform
3+
from twilio import __version__
4+
from twilio.base.exceptions import TwilioException
5+
from twilio.base.obsolete import obsolete_client
6+
from twilio.http.http_client import TwilioHttpClient
7+
from urllib.parse import (
8+
urlparse,
9+
urlunparse,
10+
)
11+
12+
13+
class ClientBase(object):
14+
""" A client for accessing the Twilio API. """
15+
16+
def __init__(self, username=None, password=None, account_sid=None, region=None,
17+
http_client=None, environment=None, edge=None,
18+
user_agent_extensions=None):
19+
"""
20+
Initializes the Twilio Client
21+
22+
:param str username: Username to authenticate with
23+
:param str password: Password to authenticate with
24+
:param str account_sid: Account SID, defaults to Username
25+
:param str region: Twilio Region to make requests to, defaults to 'us1' if an edge is provided
26+
:param HttpClient http_client: HttpClient, defaults to TwilioHttpClient
27+
:param dict environment: Environment to look for auth details, defaults to os.environ
28+
:param str edge: Twilio Edge to make requests to, defaults to None
29+
:param list[str] user_agent_extensions: Additions to the user agent string
30+
31+
:returns: Twilio Client
32+
:rtype: twilio.rest.Client
33+
"""
34+
environment = environment or os.environ
35+
36+
self.username = username or environment.get('TWILIO_ACCOUNT_SID')
37+
""" :type : str """
38+
self.password = password or environment.get('TWILIO_AUTH_TOKEN')
39+
""" :type : str """
40+
self.account_sid = account_sid or self.username
41+
""" :type : str """
42+
self.edge = edge or environment.get('TWILIO_EDGE')
43+
""" :type : str """
44+
self.region = region or environment.get('TWILIO_REGION')
45+
""" :type : str """
46+
self.user_agent_extensions = user_agent_extensions or []
47+
""" :type : list[str] """
48+
49+
if not self.username or not self.password:
50+
raise TwilioException("Credentials are required to create a TwilioClient")
51+
52+
self.auth = (self.username, self.password)
53+
""" :type : tuple(str, str) """
54+
self.http_client = http_client or TwilioHttpClient()
55+
""" :type : HttpClient """
56+
57+
def request(self, method, uri, params=None, data=None, headers=None, auth=None,
58+
timeout=None, allow_redirects=False):
59+
"""
60+
Makes a request to the Twilio API using the configured http client
61+
Authentication information is automatically added if none is provided
62+
63+
:param str method: HTTP Method
64+
:param str uri: Fully qualified url
65+
:param dict[str, str] params: Query string parameters
66+
:param dict[str, str] data: POST body data
67+
:param dict[str, str] headers: HTTP Headers
68+
:param tuple(str, str) auth: Authentication
69+
:param int timeout: Timeout in seconds
70+
:param bool allow_redirects: Should the client follow redirects
71+
72+
:returns: Response from the Twilio API
73+
:rtype: twilio.http.response.Response
74+
"""
75+
auth = auth or self.auth
76+
headers = headers or {}
77+
78+
pkg_version = __version__
79+
os_name = platform.system()
80+
os_arch = platform.machine()
81+
python_version = platform.python_version()
82+
headers['User-Agent'] = 'twilio-python/{} ({} {}) Python/{}'.format(
83+
pkg_version,
84+
os_name,
85+
os_arch,
86+
python_version,
87+
)
88+
for extension in self.user_agent_extensions:
89+
headers['User-Agent'] += ' {}'.format(extension)
90+
headers['X-Twilio-Client'] = 'python-{}'.format(__version__)
91+
headers['Accept-Charset'] = 'utf-8'
92+
93+
if method == 'POST' and 'Content-Type' not in headers:
94+
headers['Content-Type'] = 'application/x-www-form-urlencoded'
95+
96+
if 'Accept' not in headers:
97+
headers['Accept'] = 'application/json'
98+
99+
uri = self.get_hostname(uri)
100+
101+
return self.http_client.request(
102+
method,
103+
uri,
104+
params=params,
105+
data=data,
106+
headers=headers,
107+
auth=auth,
108+
timeout=timeout,
109+
allow_redirects=allow_redirects
110+
)
111+
112+
def get_hostname(self, uri):
113+
"""
114+
Determines the proper hostname given edge and region preferences
115+
via client configuration or uri.
116+
117+
:param str uri: Fully qualified url
118+
119+
:returns: The final uri used to make the request
120+
:rtype: str
121+
"""
122+
if not self.edge and not self.region:
123+
return uri
124+
125+
parsed_url = urlparse(uri)
126+
pieces = parsed_url.netloc.split('.')
127+
prefix = pieces[0]
128+
suffix = '.'.join(pieces[-2:])
129+
region = None
130+
edge = None
131+
if len(pieces) == 4:
132+
# product.region.twilio.com
133+
region = pieces[1]
134+
elif len(pieces) == 5:
135+
# product.edge.region.twilio.com
136+
edge = pieces[1]
137+
region = pieces[2]
138+
139+
edge = self.edge or edge
140+
region = self.region or region or (edge and 'us1')
141+
142+
parsed_url = parsed_url._replace(
143+
netloc='.'.join([part for part in [prefix, edge, region, suffix] if part])
144+
)
145+
return urlunparse(parsed_url)
146+
147+
def __repr__(self):
148+
"""
149+
Provide a friendly representation
150+
151+
:returns: Machine friendly representation
152+
:rtype: str
153+
"""
154+
return '<Twilio {}>'.format(self.account_sid)

0 commit comments

Comments
 (0)