forked from pennersr/django-allauth
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(socialaccount): Add provider TrainingPeaks
* Add TrainingPeaks provider * Add convenience method api_hostname * Add response.raise_for_status() * Rename TrainingPeaksOAuth2Adapter
- Loading branch information
Showing
9 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from allauth.socialaccount.providers.base import ProviderAccount | ||
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider | ||
|
||
|
||
class TrainingPeaksAccount(ProviderAccount): | ||
def get_profile_url(self): | ||
return "https://app.trainingpeaks.com" | ||
|
||
def get_avatar_url(self): | ||
return None | ||
|
||
def to_str(self): | ||
name = self.account.extra_data.get("FirstName") + \ | ||
" " + self.account.extra_data.get("LastName") | ||
if name != " ": | ||
return name | ||
return super(TrainingPeaksAccount, self).to_str() | ||
|
||
class TrainingPeaksProvider(OAuth2Provider): | ||
id = "trainingpeaks" | ||
name = "TrainingPeaks" | ||
account_class = TrainingPeaksAccount | ||
|
||
def extract_uid(self, data): | ||
return data.get("Id") | ||
|
||
def extract_common_fields(self, data): | ||
extra_common = super(TrainingPeaksProvider, self).extract_common_fields(data) | ||
firstname = data.get("FirstName") | ||
lastname = data.get("LastName") | ||
# fallback username as there is actually no Username in response | ||
username = firstname.strip().lower() + "." + lastname.strip().lower() | ||
name = " ".join(part for part in (firstname, lastname) if part) | ||
extra_common.update( | ||
username=data.get("username", username), | ||
email=data.get("Email"), | ||
first_name=firstname, | ||
last_name=lastname, | ||
name=name.strip(), | ||
) | ||
return extra_common | ||
|
||
def get_default_scope(self): | ||
return ["athlete:profile"] | ||
|
||
provider_classes = [TrainingPeaksProvider] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Run just this suite: | ||
python manage.py test allauth.socialaccount.providers.trainingpeaks.tests.TrainingPeaksTests | ||
""" | ||
from __future__ import unicode_literals | ||
|
||
from collections import namedtuple | ||
|
||
from django.contrib.auth.models import User | ||
from django.test.utils import override_settings | ||
|
||
from allauth.socialaccount.models import SocialAccount | ||
from allauth.socialaccount.tests import OAuth2TestsMixin | ||
from allauth.tests import MockedResponse, TestCase | ||
|
||
from .provider import TrainingPeaksProvider | ||
from .views import TrainingPeaksOAuth2Adapter | ||
|
||
|
||
class TrainingPeaksTests(OAuth2TestsMixin, TestCase): | ||
provider_id = TrainingPeaksProvider.id | ||
|
||
def get_mocked_response(self): | ||
return MockedResponse( | ||
200, | ||
"""{ | ||
"Id": 123456, | ||
"FirstName": "John", | ||
"LastName": "Doe", | ||
"Email": "[email protected]", | ||
"DateOfBirth": "1986-02-01T00:00:00", | ||
"CoachedBy": 987654, | ||
"Weight": 87.5223617553711 | ||
}""", | ||
) # noqa | ||
|
||
def get_login_response_json(self, with_refresh_token=True): | ||
rtoken = "" | ||
if with_refresh_token: | ||
rtoken = ',"refresh_token": "testrf"' | ||
return ( | ||
"""{ | ||
"access_token" : "testac", | ||
"token_type" : "bearer", | ||
"expires_in" : 600, | ||
"scope": "scopes granted" | ||
%s }""" | ||
% rtoken | ||
) | ||
|
||
def test_default_use_sandbox_uri(self): | ||
adapter = TrainingPeaksOAuth2Adapter(None) | ||
self.assertTrue('.sandbox.' in adapter.authorize_url) | ||
self.assertTrue('.sandbox.' in adapter.access_token_url) | ||
self.assertTrue('.sandbox.' in adapter.profile_url) | ||
|
||
@override_settings(SOCIALACCOUNT_PROVIDERS={ | ||
'trainingpeaks': { | ||
'USE_PRODUCTION': True | ||
} | ||
}) | ||
def test_use_production_uri(self): | ||
adapter = TrainingPeaksOAuth2Adapter(None) | ||
self.assertFalse('.sandbox.' in adapter.authorize_url) | ||
self.assertFalse('.sandbox.' in adapter.access_token_url) | ||
self.assertFalse('.sandbox.' in adapter.profile_url) | ||
|
||
def test_scope_from_default(self): | ||
Request = namedtuple('request', ['GET']) | ||
mock_request = Request(GET={}) | ||
scope = self.provider.get_scope(mock_request) | ||
self.assertTrue('athlete:profile' in scope) | ||
|
||
@override_settings(SOCIALACCOUNT_PROVIDERS={ | ||
'trainingpeaks': { | ||
'SCOPE': ['athlete:profile', 'workouts', 'workouts:wod'] | ||
} | ||
}) | ||
def test_scope_from_settings(self): | ||
Request = namedtuple('request', ['GET']) | ||
mock_request = Request(GET={}) | ||
scope = self.provider.get_scope(mock_request) | ||
for item in ('athlete:profile', 'workouts', 'workouts:wod'): | ||
self.assertTrue(item in scope) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns | ||
|
||
from .provider import TrainingPeaksProvider | ||
|
||
|
||
urlpatterns = default_urlpatterns(TrainingPeaksProvider) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import requests | ||
|
||
from allauth.socialaccount import app_settings | ||
from allauth.socialaccount.providers.oauth2.views import ( | ||
OAuth2Adapter, | ||
OAuth2CallbackView, | ||
OAuth2LoginView, | ||
) | ||
|
||
from .provider import TrainingPeaksProvider | ||
|
||
|
||
class TrainingPeaksOAuth2Adapter(OAuth2Adapter): | ||
# https://github.com/TrainingPeaks/PartnersAPI/wiki/OAuth | ||
provider_id = TrainingPeaksProvider.id | ||
|
||
def get_settings(self): | ||
""" Provider settings """ | ||
return app_settings.PROVIDERS.get(self.provider_id, {}) | ||
|
||
def get_hostname(self): | ||
""" Return hostname depending on sandbox seting """ | ||
settings = self.get_settings() | ||
if (settings.get('USE_PRODUCTION')): | ||
return 'trainingpeaks.com' | ||
return 'sandbox.trainingpeaks.com' | ||
|
||
@property | ||
def access_token_url(self): | ||
return "https://oauth." + self.get_hostname() + "/oauth/token" | ||
|
||
@property | ||
def authorize_url(self): | ||
return "https://oauth." + self.get_hostname() + "/OAuth/Authorize" | ||
|
||
@property | ||
def profile_url(self): | ||
return "https://api." + self.get_hostname() + "/v1/athlete/profile" | ||
|
||
@property | ||
def api_hostname(self): | ||
""" Return https://api.hostname.tld """ | ||
return "https://api." + self.get_hostname() | ||
|
||
# https://oauth.sandbox.trainingpeaks.com/oauth/deauthorize | ||
|
||
|
||
scope_delimiter = " " | ||
|
||
def complete_login(self, request, app, token, **kwargs): | ||
headers = {"Authorization": "Bearer {0}".format(token.token)} | ||
response = requests.get(self.profile_url, headers=headers) | ||
response.raise_for_status() | ||
extra_data = response.json() | ||
return self.get_provider().sociallogin_from_response(request, extra_data) | ||
|
||
|
||
oauth2_login = OAuth2LoginView.adapter_view(TrainingPeaksOAuth2Adapter) | ||
oauth2_callback = OAuth2CallbackView.adapter_view(TrainingPeaksOAuth2Adapter) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -201,6 +201,8 @@ Supported Providers | |
|
||
- Telegram | ||
|
||
- TrainingPeaks (OAuth2) | ||
|
||
- Trello (OAuth) | ||
|
||
- Tumblr (OAuth) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters