Skip to content

Commit 146817d

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 pass any date. Signed-off-by: Santiago Dueñas <sduenas@bitergia.com>
1 parent be1de1b commit 146817d

3 files changed

Lines changed: 80 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: 41 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,59 @@ 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+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
122+
def test_parse_from_date(self, mock_datetime_now):
123+
"""Check if from_date parameter is parsed correctly"""
110124

111125
# Check from_date is parsed correctly
112126
importer = EclipseFoundationAccountsImporter(
113127
ctx=self.ctx,
114-
url=url,
115-
from_date="2023-12-01"
128+
url="https://test-url.com/",
129+
from_date="2025-12-01"
116130
)
117-
self.assertEqual(importer.from_date, datetime.datetime(year=2023,
131+
self.assertEqual(importer.from_date, datetime.datetime(year=2025,
118132
month=12,
119133
day=1,
120134
tzinfo=tzutc()))
121135

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

125150
self.assertEqual(EclipseFoundationAccountsImporter.NAME, "EclipseFoundation")
126151

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

131157
# Set up a mock HTTP server
132158
requests, bodies = setup_mock_server()
@@ -194,14 +220,15 @@ def test_import_identities(self, mock_login):
194220

195221
@httpretty.activate
196222
@patch('sortinghat.core.importer.backends.eclipse.EclipseFoundationAPIClient.login', return_value="mocked_login")
197-
def test_import_no_identities(self, mock_login):
223+
@patch('sortinghat.core.importer.backends.eclipse.datetime_utcnow', return_value=MOCK_DATETIME_NOW)
224+
def test_import_no_identities(self, mock_login, mock_datetime_now):
198225
# Set up a mock HTTP server
199226
requests, bodies = setup_mock_server()
200227

201228
importer = EclipseFoundationAccountsImporter(
202229
ctx=self.ctx,
203230
url=None,
204-
from_date=datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=tzutc())
231+
from_date="2100-1-1"
205232
)
206233

207234
n = importer.import_identities()
@@ -211,7 +238,8 @@ def test_import_no_identities(self, mock_login):
211238

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

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

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

236265
# Set up a mock HTTP server
237266
setup_mock_server()

0 commit comments

Comments
 (0)