mirror of
https://github.com/bckelley/tconnectsync.git
synced 2026-08-24 03:34:12 -05:00
Bump supported software version and display login error message
This commit is contained in:
@@ -127,7 +127,7 @@ def split_days_range(start_a, end_a, days: int = 5) -> List[Tuple[str, str]]:
|
||||
class ApiException(Exception):
|
||||
def __init__(self, status_code, text, *args, **kwargs):
|
||||
self.status_code = status_code
|
||||
super().__init__('%s (HTTP %s)' % (text, status_code), *args, **kwargs)
|
||||
super().__init__('%s%s' % (text, ' (HTTP %s)' % status_code if status_code else ''), *args, **kwargs)
|
||||
|
||||
class ApiLoginException(ApiException):
|
||||
pass
|
||||
@@ -5,7 +5,7 @@ import logging
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from ..util import timeago
|
||||
from ..util import timeago, cap_length
|
||||
from .common import parse_date, base_headers, base_session, ApiException, ApiLoginException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -14,7 +14,7 @@ class ControlIQApi:
|
||||
BASE_URL = 'https://tdcservices.tandemdiabetes.com/'
|
||||
LOGIN_URL = 'https://tconnect.tandemdiabetes.com/login.aspx?ReturnUrl=%2f'
|
||||
|
||||
LAST_CONFIRMED_SOFTWARE_VERSION = 't:connect 7.14.0.1'
|
||||
LAST_CONFIRMED_SOFTWARE_VERSION = 't:connect 7.15.0.1'
|
||||
|
||||
userGuid = None
|
||||
accessToken = None
|
||||
@@ -34,12 +34,20 @@ class ControlIQApi:
|
||||
data = self._build_login_data(email, password, soup)
|
||||
|
||||
req = s.post(self.LOGIN_URL, data=data, headers={'Referer': self.LOGIN_URL, **base_headers()}, allow_redirects=False)
|
||||
# HTTP 200 is reported when credentials are incorrect
|
||||
if req.status_code == 200:
|
||||
login_error = self._find_login_error(req.text)
|
||||
if not login_error:
|
||||
login_error = 'Check your login credentials.'
|
||||
raise ApiLoginException(None, 'Error logging in to t:connect: %s' % login_error)
|
||||
|
||||
if req.status_code != 302:
|
||||
raise ApiLoginException(req.status_code, 'Error logging in to t:connect. Check your login credentials.')
|
||||
raise ApiLoginException(req.status_code, 'Error logging in to t:connect')
|
||||
|
||||
|
||||
fwd = s.post(urllib.parse.urljoin(self.LOGIN_URL, req.headers['Location']), cookies=req.cookies, headers=base_headers())
|
||||
if fwd.status_code != 200:
|
||||
logger.warn("Received non-HTTP 200: %s" % req.text)
|
||||
raise ApiException(fwd.status_code, 'Error retrieving t:connect login cookies.')
|
||||
|
||||
self.userGuid = req.cookies['UserGUID']
|
||||
@@ -79,6 +87,14 @@ class ControlIQApi:
|
||||
"txtLoginPassword_ClientState": '{"enabled":true,"emptyMessage":"","validationText":"%s","valueAsString":"%s","lastSetTextBoxValue":"%s"}' % (password, password, password)
|
||||
}
|
||||
|
||||
def _find_login_error(self, text):
|
||||
try:
|
||||
soup = BeautifulSoup(text, features='lxml')
|
||||
notice_error = soup.select_one(".notice_error").text.strip()
|
||||
return notice_error
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def needs_relogin(self):
|
||||
diff = (arrow.get(self.accessTokenExpiresAt) - arrow.get())
|
||||
return (diff.seconds <= 5 * 60)
|
||||
|
||||
@@ -28,4 +28,9 @@ def removesuffix(input_string, suffix):
|
||||
def removeprefix(input_string, prefix):
|
||||
if prefix and input_string.startswith(prefix):
|
||||
return input_string[len(prefix):]
|
||||
return input_string
|
||||
return input_string
|
||||
|
||||
def cap_length(text, maxlen):
|
||||
if not text or len(text) <= maxlen:
|
||||
return text
|
||||
return '%s[...]%s' % (text[:maxlen//2], text[maxlen//-2:])
|
||||
@@ -111,12 +111,65 @@ class TestControlIQApi(unittest.TestCase):
|
||||
|
||||
text=post_callback)
|
||||
|
||||
self.assertRaises(ApiLoginException, ciq.login, 'email@email.com', 'password')
|
||||
self.assertRaisesRegex(ApiLoginException, 'Error logging in to t:connect: Check your login credentials.', ciq.login, 'email@email.com', 'password')
|
||||
|
||||
self.assertIsNone(ciq.userGuid)
|
||||
self.assertIsNone(ciq.accessToken)
|
||||
self.assertIsNone(ciq.accessTokenExpiresAt)
|
||||
|
||||
def test_login_invalid_credentials_parsed_message(self):
|
||||
ciq = ControlIQApi()
|
||||
ciq.LOGIN_URL = RealControlIQApi.LOGIN_URL
|
||||
ciq.login = lambda email, password: RealControlIQApi.login(ciq, email, password)
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
m.get('https://tconnect.tandemdiabetes.com/login.aspx?ReturnUrl=%2f',
|
||||
request_headers=base_headers(),
|
||||
|
||||
text=self.LOGIN_HTML)
|
||||
|
||||
def post_callback(request, context):
|
||||
context.status_code = 200
|
||||
return '<html><body><div class="notice_error" id="literalMessage" style="">The email address or password you entered is invalid. Please re-enter and try again.</div></body></html>'
|
||||
|
||||
m.post('https://tconnect.tandemdiabetes.com/login.aspx?ReturnUrl=%2f',
|
||||
request_headers={'Referer': ciq.LOGIN_URL, **base_headers()},
|
||||
|
||||
text=post_callback)
|
||||
|
||||
self.assertRaisesRegex(ApiLoginException, 'Error logging in to t:connect: The email address or password you entered is invalid. Please re-enter and try again.', ciq.login, 'email@email.com', 'password')
|
||||
|
||||
self.assertIsNone(ciq.userGuid)
|
||||
self.assertIsNone(ciq.accessToken)
|
||||
self.assertIsNone(ciq.accessTokenExpiresAt)
|
||||
|
||||
def test_login_unexpected_http_code(self):
|
||||
ciq = ControlIQApi()
|
||||
ciq.LOGIN_URL = RealControlIQApi.LOGIN_URL
|
||||
ciq.login = lambda email, password: RealControlIQApi.login(ciq, email, password)
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
m.get('https://tconnect.tandemdiabetes.com/login.aspx?ReturnUrl=%2f',
|
||||
request_headers=base_headers(),
|
||||
|
||||
text=self.LOGIN_HTML)
|
||||
|
||||
def post_callback(request, context):
|
||||
context.status_code = 500
|
||||
return '<html><body>...</body></html>'
|
||||
|
||||
m.post('https://tconnect.tandemdiabetes.com/login.aspx?ReturnUrl=%2f',
|
||||
request_headers={'Referer': ciq.LOGIN_URL, **base_headers()},
|
||||
|
||||
text=post_callback)
|
||||
|
||||
self.assertRaisesRegex(ApiLoginException, 'Error logging in to t:connect \(HTTP 500\)', ciq.login, 'email@email.com', 'password')
|
||||
|
||||
self.assertIsNone(ciq.userGuid)
|
||||
self.assertIsNone(ciq.accessToken)
|
||||
self.assertIsNone(ciq.accessTokenExpiresAt)
|
||||
|
||||
|
||||
def fake_get_with_http_code(self, http_code, expected_endpoint, num_times):
|
||||
tries = 0
|
||||
def fake_get(endpoint, query):
|
||||
|
||||
Reference in New Issue
Block a user