Skip to content

Commit 05235f6

Browse files
committed
Fix BadRequest due to invalid epoch time
The API only allows to fetch identities updated since the last year. The importer allowed to give any date. Signed-off-by: Santiago Dueñas <sduenas@bitergia.com>
1 parent be1de1b commit 05235f6

3 files changed

Lines changed: 81 additions & 19 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
title: Minimum value for 'from_date' error
3+
category: fixed
4+
author: Santiago Dueñas <sduenas@bitergia.com>
5+
issue: null
6+
notes: >
7+
EclipseFoundation API only allows to fetch identities
8+
updated since the last year. The backend allowed dates
9+
before that but the server raised BadRequest error.

sortinghat/core/importer/backends/eclipse.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import logging
1919

20+
import dateutil.relativedelta
2021
import requests
2122

2223
from django.conf import settings
@@ -27,7 +28,10 @@
2728
OAuth2Client
2829
)
2930

30-
from grimoirelab_toolkit.datetime import str_to_datetime
31+
from grimoirelab_toolkit.datetime import (
32+
str_to_datetime,
33+
datetime_utcnow
34+
)
3135
from sortinghat.core.importer.backend import IdentitiesImporter
3236
from sortinghat.core.importer.models import (
3337
Individual,
@@ -53,24 +57,43 @@ class EclipseFoundationAccountsImporter(IdentitiesImporter):
5357
5458
The importer fetches and stores in the database identities
5559
created or updated after the given date (`from_date`) parameter.
56-
When no date is given, it will import all the identities available
57-
in the platform.
60+
Currently, it can only import identities updated since a year ago.
61+
When no date is given, it will import all the identities updated
62+
since last year.
5863
5964
Each individual created after importing will have two identities:
6065
one with source set as 'eclipsefdn' that includes their name, email
6166
and username as it comes from the platform, and a second one with
6267
source 'github' only when the github user was set by the identity
6368
on the Eclipse profile.
69+
70+
:param ctx: SortingHat context
71+
:param url: not used on this importer
72+
:param from_date: start fetching identities updated from this date
73+
74+
:raises ValueError: when the date is older than one year ago
6475
"""
6576
NAME = "EclipseFoundation"
6677

6778
def __init__(self, ctx, url, from_date=None):
6879
super().__init__(ctx, url)
6980

70-
if isinstance(from_date, str):
71-
from_date = str_to_datetime(from_date)
81+
min_date = datetime_utcnow() - dateutil.relativedelta.relativedelta(years=1)
7282

73-
self.from_date = from_date
83+
if not from_date:
84+
self.from_date = min_date
85+
elif isinstance(from_date, str):
86+
self.from_date = str_to_datetime(from_date)
87+
else:
88+
self.from_date = from_date
89+
90+
if self.from_date < min_date:
91+
msg = (
92+
"Invalid 'from_date' value. It can only import identities updated since a year ago."
93+
"from_date=" + from_date
94+
)
95+
logger.error(msg)
96+
raise ValueError(msg)
7497

7598
def get_individuals(self):
7699
"""Get the individuals from the Eclipse Foundation platform."""
@@ -81,7 +104,7 @@ def get_individuals(self):
81104
client = EclipseFoundationAPIClient()
82105
client.login(user_id, password)
83106

84-
epoch = int(self.from_date.timestamp()) if self.from_date else 1
107+
epoch = int(self.from_date.timestamp())
85108

86109
# Fetch accounts pages
87110
for account in client.fetch_accounts(epoch=epoch):

tests/test_eclipse.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from unittest.mock import patch
55

66
import httpretty
7+
8+
from dateutil.relativedelta import relativedelta
79
from dateutil.tz import tzutc
810
from django.test import TestCase
911
from django.contrib.auth import get_user_model
@@ -20,6 +22,9 @@
2022
OAUTH_TOKEN_ENDPOINT = "https://accounts.eclipse.org/oauth2/token"
2123

2224

25+
MOCK_DATETIME_NOW = datetime.datetime(2025, 1, 1, tzinfo=tzutc())
26+
27+
2328
def read_file(filename, mode='r'):
2429
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), filename), mode) as f:
2530
content = f.read()
@@ -95,38 +100,60 @@ def setUp(self):
95100
self.user = get_user_model().objects.create(username='test')
96101
self.ctx = SortingHatContext(self.user)
97102

98-
def test_initialization(self):
103+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
104+
def test_initialization(self, mock_datetime_now):
99105
"""Test whether attributes are initialized"""
100106

101107
url = "https://test-url.com/"
102108

103-
importer = EclipseFoundationAccountsImporter(ctx=self.ctx,
104-
url=url)
109+
importer = EclipseFoundationAccountsImporter(
110+
ctx=self.ctx,
111+
url=url
112+
)
105113

106114
self.assertEqual(importer.url, url)
107115
self.assertEqual(importer.ctx, self.ctx)
108116
self.assertEqual(importer.NAME, "EclipseFoundation")
109-
self.assertIsNone(importer.from_date)
117+
118+
# 'from_date' is 1 year before the current date (MOCK_DATETIME_NOW)
119+
self.assertEqual(importer.from_date, mock_datetime_now.return_value - relativedelta(years=1)
120+
)
121+
122+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
123+
def test_parse_from_date(self, mock_datetime_now):
124+
"""Check if from_date parameter is parsed correctly"""
110125

111126
# Check from_date is parsed correctly
112127
importer = EclipseFoundationAccountsImporter(
113128
ctx=self.ctx,
114-
url=url,
115-
from_date="2023-12-01"
129+
url="https://test-url.com/",
130+
from_date="2025-12-01"
116131
)
117-
self.assertEqual(importer.from_date, datetime.datetime(year=2023,
132+
self.assertEqual(importer.from_date, datetime.datetime(year=2025,
118133
month=12,
119134
day=1,
120135
tzinfo=tzutc()))
121136

137+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
138+
def test_from_date_older_than_one_year(self, mock_datetime_now):
139+
"""Check if an error is raised when the from_date is invalid"""
140+
141+
with self.assertRaises(ValueError):
142+
_ = EclipseFoundationAccountsImporter(
143+
ctx=self.ctx,
144+
url="https://test-url.com/",
145+
from_date="2000-01-01"
146+
)
147+
122148
def test_backend_name(self):
123149
"""Test whether the NAME of the backend is right"""
124150

125151
self.assertEqual(EclipseFoundationAccountsImporter.NAME, "EclipseFoundation")
126152

127153
@httpretty.activate
128154
@patch('sortinghat.core.importer.backends.eclipse.EclipseFoundationAPIClient.login', return_value="mocked_login")
129-
def test_import_identities(self, mock_login):
155+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
156+
def test_import_identities(self, mock_login, mock_datetime_now):
130157

131158
# Set up a mock HTTP server
132159
requests, bodies = setup_mock_server()
@@ -194,14 +221,15 @@ def test_import_identities(self, mock_login):
194221

195222
@httpretty.activate
196223
@patch('sortinghat.core.importer.backends.eclipse.EclipseFoundationAPIClient.login', return_value="mocked_login")
197-
def test_import_no_identities(self, mock_login):
224+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
225+
def test_import_no_identities(self, mock_login, mock_datetime_now):
198226
# Set up a mock HTTP server
199227
requests, bodies = setup_mock_server()
200228

201229
importer = EclipseFoundationAccountsImporter(
202230
ctx=self.ctx,
203231
url=None,
204-
from_date=datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=tzutc())
232+
from_date="2100-1-1"
205233
)
206234

207235
n = importer.import_identities()
@@ -211,7 +239,8 @@ def test_import_no_identities(self, mock_login):
211239

212240
@httpretty.activate
213241
@patch('sortinghat.core.importer.backends.eclipse.EclipseFoundationAPIClient.login', return_value="mocked_login")
214-
def test_import_merge_identities(self, mock_login):
242+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
243+
def test_import_merge_identities(self, mock_login, mock_datetime_now):
215244

216245
# Add individuals that share email and github handle
217246
api.add_identity(self.ctx, source='github', username='jsmith')
@@ -231,7 +260,8 @@ def test_import_merge_identities(self, mock_login):
231260

232261
@httpretty.activate
233262
@patch('sortinghat.core.importer.backends.eclipse.EclipseFoundationAPIClient.login', return_value="mocked_login")
234-
def test_import_enrollments(self, mock_login):
263+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
264+
def test_import_enrollments(self, mock_login, mock_datetime_now):
235265

236266
# Set up a mock HTTP server
237267
setup_mock_server()

0 commit comments

Comments
 (0)