Skip to content

Commit 8a96003

Browse files
committed
feat: add LTI tools for LTI APIs in edX
1 parent 56ab615 commit 8a96003

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

edx_api/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .grades import UserCurrentGrades
1616
from .user_info import UserInfo
1717
from .user_validation import UserValidation
18+
from .lti_tools import LTITools
1819

1920

2021
class EdxApi:
@@ -123,3 +124,9 @@ def user_validation(self):
123124
def course_runs(self):
124125
"""Course runs management API (Works with CMS)"""
125126
return CourseRuns(self.get_requester(token_type="jwt"), self.base_url)
127+
128+
@property
129+
def lti_tools(self):
130+
"""LTI Tools API"""
131+
return LTITools(self.get_requester(), self.base_url)
132+

edx_api/lti_tools/__init__.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Client for LTI Tools API"""
2+
from urllib.parse import urljoin
3+
4+
5+
class LTITools:
6+
"""
7+
Open edX LTI Tools client
8+
"""
9+
10+
def __init__(self, requester, base_url):
11+
"""
12+
Args:
13+
requester (Requester): an authenticated objects for requests to edX
14+
base_url (str): string representing the base URL of an edX LMS instance
15+
"""
16+
self.requester = requester
17+
self.base_url = base_url
18+
19+
20+
def fix_lti_user(self, email):
21+
"""
22+
Fixes an LTI user with duplicate email
23+
24+
Args:
25+
email (str): Email of the Application user
26+
27+
Returns:
28+
bool: True if the user was fixed successfully, False otherwise
29+
"""
30+
request_data = {"email": email}
31+
32+
# the request is done on behalf of the current logged in user
33+
return self.requester.post(
34+
urljoin(
35+
self.base_url,
36+
'/api/lti-user-fix/'
37+
),
38+
json=request_data)

edx_api/lti_tools/init_test.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Tests for LTI Tools API client"""
2+
3+
from unittest.mock import Mock
4+
from urllib.parse import urljoin
5+
6+
from edx_api.lti_tools import LTITools
7+
8+
9+
class TestLTITools:
10+
"""Tests for LTITools class"""
11+
12+
def setup_method(self):
13+
"""Set up test fixtures"""
14+
self.requester = Mock()
15+
self.base_url = "https://example.edx.org"
16+
self.client = LTITools(self.requester, self.base_url)
17+
18+
def test_init(self):
19+
"""Test LTITools initialization"""
20+
assert self.client.requester == self.requester
21+
assert self.client.base_url == self.base_url
22+
23+
def test_fix_lti_user_success(self):
24+
"""Test fix_lti_user with successful response"""
25+
26+
expected_response = Mock()
27+
self.requester.post.return_value = expected_response
28+
29+
result = self.client.fix_lti_user(email)
30+
31+
self.requester.post.assert_called_once_with(
32+
urljoin(self.base_url, '/api/lti-user-fix/'),
33+
json={"email": email}
34+
)
35+
assert result == expected_response
36+
37+
def test_fix_lti_user_with_different_credentials(self):
38+
"""Test fix_lti_user with email"""
39+
40+
expected_response = Mock()
41+
self.requester.post.return_value = expected_response
42+
43+
result = self.client.fix_lti_user(email)
44+
45+
self.requester.post.assert_called_once_with(
46+
urljoin(self.base_url, '/api/lti-user-fix/'),
47+
json={"email": email}
48+
)
49+
assert result == expected_response
50+
51+
def test_fix_lti_user_url_construction(self):
52+
"""Test that the correct URL is constructed"""
53+
54+
55+
self.client.fix_lti_user(email)
56+
57+
call_args = self.requester.post.call_args
58+
expected_url = urljoin(self.base_url, '/api/lti-user-fix/')
59+
assert call_args[0][0] == expected_url
60+
61+
def test_fix_lti_user_request_data_format(self):
62+
"""Test that request data is formatted correctly"""
63+
64+
65+
self.client.fix_lti_user(email)
66+
67+
call_args = self.requester.post.call_args
68+
assert call_args[1]['json'] == {"email": email}

0 commit comments

Comments
 (0)