Skip to content

Commit 02fb1c4

Browse files
authored
Merge pull request #668 from plotly/validate-signin
Validate signin
2 parents 193f613 + 279a648 commit 02fb1c4

File tree

7 files changed

+106
-32
lines changed

7 files changed

+106
-32
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ failures. Previously either a `PlotlyError`, `PlotlyRequestException`, or a
1010
`requests.exceptions.ReqestException` could be raised. In particular, scripts
1111
which depend on `try-except` blocks containing network requests should be
1212
revisited.
13+
- `plotly.py:sign_in` now validates to the plotly server specified in your
14+
config. If it cannot make a successful request, it raises a `PlotlyError`.
1315

1416
### Deprecated
1517
- `plotly.tools.FigureFactory`. Use `plotly.figure_factory.*`.

plotly/api/v2/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from __future__ import absolute_import
22

3-
from plotly.api.v2 import files, folders, grids, images, plot_schema, plots
3+
from plotly.api.v2 import (files, folders, grids, images, plot_schema, plots,
4+
users)

plotly/api/v2/users.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Interface to Plotly's /v2/files endpoints."""
2+
from __future__ import absolute_import
3+
4+
from plotly.api.v2.utils import build_url, request
5+
6+
RESOURCE = 'users'
7+
8+
9+
def current():
10+
"""
11+
Retrieve information on the logged-in user from Plotly.
12+
13+
:returns: (requests.Response) Returns response directly from requests.
14+
15+
"""
16+
url = build_url(RESOURCE, route='current')
17+
return request('get', url)

plotly/plotly/plotly.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@
2424
import six.moves
2525
from requests.compat import json as _json
2626

27-
from plotly import exceptions, tools, utils, files
27+
from plotly import exceptions, files, session, tools, utils
2828
from plotly.api import v1, v2
2929
from plotly.plotly import chunked_requests
30-
from plotly.session import (sign_in, update_session_plot_options,
31-
get_session_plot_options)
3230
from plotly.grid_objs import Grid, Column
3331

3432
# This is imported like this for backwards compat. Careful if changing.
@@ -50,8 +48,16 @@
5048

5149

5250
# don't break backwards compatibility
53-
sign_in = sign_in
54-
update_plot_options = update_session_plot_options
51+
def sign_in(username, api_key, **kwargs):
52+
session.sign_in(username, api_key, **kwargs)
53+
try:
54+
# The only way this can succeed is if the user can be authenticated
55+
# with the given, username, api_key, and plotly_api_domain.
56+
v2.users.current()
57+
except exceptions.PlotlyRequestError:
58+
raise exceptions.PlotlyError('Sign in failed.')
59+
60+
update_plot_options = session.update_session_plot_options
5561

5662

5763
def _plot_option_logic(plot_options_from_call_signature):
@@ -66,7 +72,7 @@ def _plot_option_logic(plot_options_from_call_signature):
6672
"""
6773
default_plot_options = copy.deepcopy(DEFAULT_PLOT_OPTIONS)
6874
file_options = tools.get_config_file()
69-
session_options = get_session_plot_options()
75+
session_options = session.get_session_plot_options()
7076
plot_options_from_call_signature = copy.deepcopy(plot_options_from_call_signature)
7177

7278
# Validate options and fill in defaults w world_readable and sharing
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from __future__ import absolute_import
2+
3+
from plotly.api.v2 import users
4+
from plotly.tests.test_core.test_api import PlotlyApiTestCase
5+
6+
7+
class UsersTest(PlotlyApiTestCase):
8+
9+
def setUp(self):
10+
super(UsersTest, self).setUp()
11+
12+
# Mock the actual api call, we don't want to do network tests here.
13+
self.request_mock = self.mock('plotly.api.v2.utils.requests.request')
14+
self.request_mock.return_value = self.get_response()
15+
16+
# Mock the validation function since we can test that elsewhere.
17+
self.mock('plotly.api.v2.utils.validate_response')
18+
19+
def test_current(self):
20+
users.current()
21+
self.request_mock.assert_called_once()
22+
args, kwargs = self.request_mock.call_args
23+
method, url = args
24+
self.assertEqual(method, 'get')
25+
self.assertEqual(
26+
url, '{}/v2/users/current'.format(self.plotly_api_domain)
27+
)
28+
self.assertNotIn('params', kwargs)

plotly/tests/test_core/test_plotly/test_credentials.py

+36-25
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,43 @@
11
from __future__ import absolute_import
22

3-
from unittest import TestCase
3+
from mock import patch
44

55
import plotly.plotly.plotly as py
66
import plotly.session as session
77
import plotly.tools as tls
8+
from plotly import exceptions
9+
from plotly.tests.utils import PlotlyTestCase
810

911

10-
def test_get_credentials():
11-
session_credentials = session.get_session_credentials()
12-
if 'username' in session_credentials:
13-
del session._session['credentials']['username']
14-
if 'api_key' in session_credentials:
15-
del session._session['credentials']['api_key']
16-
creds = py.get_credentials()
17-
file_creds = tls.get_credentials_file()
18-
print(creds)
19-
print(file_creds)
20-
assert creds == file_creds
12+
class TestSignIn(PlotlyTestCase):
2113

14+
def setUp(self):
15+
super(TestSignIn, self).setUp()
16+
patcher = patch('plotly.api.v2.users.current')
17+
self.users_current_mock = patcher.start()
18+
self.addCleanup(patcher.stop)
2219

23-
def test_sign_in():
24-
un = 'anyone'
25-
ak = 'something'
26-
# TODO, add this!
27-
# si = ['this', 'and-this']
28-
py.sign_in(un, ak)
29-
creds = py.get_credentials()
30-
assert creds['username'] == un
31-
assert creds['api_key'] == ak
32-
# TODO, and check it!
33-
# assert creds['stream_ids'] == si
20+
def test_get_credentials(self):
21+
session_credentials = session.get_session_credentials()
22+
if 'username' in session_credentials:
23+
del session._session['credentials']['username']
24+
if 'api_key' in session_credentials:
25+
del session._session['credentials']['api_key']
26+
creds = py.get_credentials()
27+
file_creds = tls.get_credentials_file()
28+
self.assertEqual(creds, file_creds)
3429

35-
36-
class TestSignIn(TestCase):
30+
def test_sign_in(self):
31+
un = 'anyone'
32+
ak = 'something'
33+
# TODO, add this!
34+
# si = ['this', 'and-this']
35+
py.sign_in(un, ak)
36+
creds = py.get_credentials()
37+
self.assertEqual(creds['username'], un)
38+
self.assertEqual(creds['api_key'], ak)
39+
# TODO, and check it!
40+
# assert creds['stream_ids'] == si
3741

3842
def test_get_config(self):
3943
plotly_domain = 'test domain'
@@ -74,3 +78,10 @@ def test_sign_in_with_config(self):
7478
self.assertEqual(
7579
config['plotly_ssl_verification'], plotly_ssl_verification
7680
)
81+
82+
def test_sign_in_cannot_validate(self):
83+
self.users_current_mock.side_effect = exceptions.PlotlyRequestError(
84+
'msg', 400, 'foobar'
85+
)
86+
with self.assertRaisesRegexp(exceptions.PlotlyError, 'Sign in failed'):
87+
py.sign_in('foo', 'bar')

plotly/tests/test_core/test_plotly/test_plot.py

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from requests.compat import json as _json
1313

1414
from unittest import TestCase
15+
from mock import patch
1516
from nose.plugins.attrib import attr
1617
from nose.tools import raises
1718

@@ -223,6 +224,14 @@ class TestPlotOptionLogic(PlotlyTestCase):
223224
{'world_readable': False, 'sharing': 'public'}
224225
)
225226

227+
def setUp(self):
228+
super(TestPlotOptionLogic, self).setUp()
229+
230+
# Make sure we don't hit sign-in validation failures.
231+
patcher = patch('plotly.api.v2.users.current')
232+
self.users_current_mock = patcher.start()
233+
self.addCleanup(patcher.stop)
234+
226235
def test_default_options(self):
227236
options = py._plot_option_logic({})
228237
config_options = tls.get_config_file()

0 commit comments

Comments
 (0)