Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions releases/unreleased/oauth2-token-refreshing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: OAuth2 token refreshing
category: added
author: Santiago Dueñas <sduenas@bitergia.com>
issue: null
notes: >
OAuth2 token will be refreshed when they expired.
52 changes: 46 additions & 6 deletions sortinghat/core/importer/backends/eclipse.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,14 @@ class EclipseFoundationAPIClient:
ECLIPSE_API_URL = "https://api.eclipse.org"
ECLIPSE_ACCOUNTS_URL = "https://accounts.eclipse.org"
OAUTH_TOKEN_ENDPOINT = "https://accounts.eclipse.org/oauth2/token"
ECLIPSE_SCOPE = "eclipsefdn_view_all_profiles"

MAX_RETRIES = 3

def __init__(self):
self.token = None
self.user_id = None
self.password = None

def login(self, user_id, password):
"""Login on the Eclipse platform.
Expand All @@ -217,10 +222,12 @@ def login(self, user_id, password):
"eclipsefdn_view_all_profiles" that will allow us to
fetch all the info about profiles/identities.
"""
self.user_id = user_id
self.password = password
self.token = self._authenticate(
user_id,
password,
"eclipsefdn_view_all_profiles",
self.user_id,
self.password,
self.ECLIPSE_SCOPE,
)

def logout(self):
Expand Down Expand Up @@ -281,8 +288,7 @@ def _fetch(self, url, params=None):
"""Generic query to Eclipse usr API."""

try:
response = requests.get(url, params=params, auth=self.token)
response.raise_for_status()
data = self._fetch_retry(url, params)
except requests.exceptions.HTTPError as error:
# Ignore 5xx errors
if 500 <= error.response.status_code < 600:
Expand All @@ -296,7 +302,41 @@ def _fetch(self, url, params=None):
else:
raise error

return response.json()
return data

def _fetch_retry(self, url, params=None):
"""Fetch URL retrying in case of 403 or 500 errors.

When getting a 403 error, the method will try to authenticate
again in case the OAuth2 token has expired.
"""
retries = 0
max_retries = self.MAX_RETRIES

while retries < max_retries:
response = requests.get(url, params=params, auth=self.token)

if response.status_code == 200:
return response.json()
elif response.status_code == 403:
# Refresh token if needed and try again
if self.token.expires_at <= datetime_utcnow():
self.token = self._authenticate(
self.user_id,
self.password,
self.ECLIPSE_SCOPE,
)
retries += 1
elif 500 <= response.status_code < 600:
# Errors could have been related to server overloading
retries += 1
else:
response.raise_for_status()

response = requests.get(url, params=params, auth=self.token)
response.raise_for_status()

return response

def _authenticate(self, client_id, client_secret, scope):
"""Authenticate using OAuth2.
Expand Down
24 changes: 6 additions & 18 deletions tests/test_eclipse.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,17 @@ def request_callback(request, uri, headers):
# John Smith profile pages
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jsmith",
body=read_file('data/eclipse_jsmith.json'))
body=read_file('data/eclipse_jsmith.json') if not raise_5xx_errors else "",
status=200 if not raise_5xx_errors else 500)
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jsmith/employment-history",
body=read_file('data/eclipse_jsmith_employment.json'))

# John Doe profile pages
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jdoe",
body=read_file('data/eclipse_jdoe.json'))
body=read_file('data/eclipse_jdoe.json') if not raise_5xx_errors else "",
status=200 if not raise_5xx_errors else 500)
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jdoe/employment-history",
body=read_file('data/eclipse_jdoe_employment.json'))
Expand All @@ -86,22 +88,8 @@ def request_callback(request, uri, headers):
body=read_file('data/eclipse_jrae.json'))
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jrae/employment-history",
body=read_file('data/eclipse_jrae_employment.json'))

# Overwrite jsmith and jdoe profile replies to raise 5xx errors
if raise_5xx_errors:
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jsmith",
body="",
status=500)
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jdoe",
body="",
status=504)
httpretty.register_uri(httpretty.GET,
ECLIPSE_API_URL + "/account/profile/jrae/employment-history",
body="",
status=504)
body=read_file('data/eclipse_jrae_employment.json') if not raise_5xx_errors else "",
status=200 if not raise_5xx_errors else 500)

return requests, bodies

Expand Down