Skip to content

Commit e9154d2

Browse files
authored
Merge pull request #673 from jsmnhou/issue/662-api-lti-resource-links
Issue/662 Add API coverage for LTI resource links
2 parents 5e4f260 + e001286 commit e9154d2

File tree

9 files changed

+217
-0
lines changed

9 files changed

+217
-0
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
- Ian Altgilbers [@altgilbers](https://github.com/altgilbers)
5454
- Ian Turgeon [@iturgeon](https://github.com/iturgeon)
5555
- [@jackrsteiner](https://github.com/jackrsteiner)
56+
- Jasmine Hou [@jsmnhou](https://github.com/jsmnhou)
5657
- John Raible [@rebelaide](https://github.com/rebelaide)
5758
- Joon Ro [@joonro](https://github.com/joonro)
5859
- Jonah Majumder [@jonahmajumder](https://github.com/jonahmajumder)

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### New Endpoint Coverage
6+
7+
- LTI Resource Links (Thanks, [@jsmnhou](https://github.com/jsmnhou))
8+
59
### Backstage
610

711
- Updated deploy Action to use more modern processes.

canvasapi/course.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from canvasapi.grading_period import GradingPeriod
2424
from canvasapi.grading_standard import GradingStandard
2525
from canvasapi.license import License
26+
from canvasapi.lti_resource_link import LTIResourceLink
2627
from canvasapi.module import Module
2728
from canvasapi.new_quiz import NewQuiz
2829
from canvasapi.outcome_import import OutcomeImport
@@ -438,6 +439,39 @@ def create_late_policy(self, **kwargs):
438439

439440
return LatePolicy(self._requester, late_policy_json["late_policy"])
440441

442+
def create_lti_resource_link(self, url, title=None, custom=None, **kwargs):
443+
"""
444+
Create a new LTI resource link.
445+
446+
:calls: `POST /api/v1/courses/:course_id/lti_resource_links \
447+
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.create>`_
448+
449+
:param url: The launch URL for the resource link.
450+
:type url: `str`
451+
:param title: The title of the resource link.
452+
:type title: `str`, optional
453+
:param custom: Custom parameters to send to the tool.
454+
:type custom: `dict`, optional
455+
456+
:rtype: :class:`canvasapi.lti_resource_link.LTIResourceLink`
457+
"""
458+
459+
if not url:
460+
raise RequiredFieldMissing("url is required as a str.")
461+
462+
kwargs["url"] = url
463+
if title:
464+
kwargs["title"] = title
465+
if custom:
466+
kwargs["custom"] = custom
467+
468+
response = self._requester.request(
469+
"POST",
470+
f"courses/{self.id}/lti_resource_links",
471+
_kwargs=combine_kwargs(**kwargs),
472+
)
473+
return LTIResourceLink(self._requester, response.json())
474+
441475
def create_module(self, module, **kwargs):
442476
"""
443477
Create a new module.
@@ -1645,6 +1679,49 @@ def get_licenses(self, **kwargs):
16451679
_kwargs=combine_kwargs(**kwargs),
16461680
)
16471681

1682+
def get_lti_resource_link(self, lti_resource_link, **kwargs):
1683+
"""
1684+
Return details about the specified resource link.
1685+
1686+
:calls: `GET /api/v1/courses/:course_id/lti_resource_links/:id \
1687+
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.show>`_
1688+
1689+
:param lti_resource_link: The object or ID of the LTI resource link.
1690+
:type lti_resource_link: :class:`canvasapi.lti_resource_link.LTIResourceLink` or int
1691+
1692+
:rtype: :class:`canvasapi.lti_resource_link.LTIResourceLink`
1693+
"""
1694+
1695+
lti_resource_link_id = obj_or_id(
1696+
lti_resource_link, "lti_resource_link", (LTIResourceLink,)
1697+
)
1698+
1699+
response = self._requester.request(
1700+
"GET",
1701+
f"courses/{self.id}/lti_resource_links/{lti_resource_link_id}",
1702+
_kwargs=combine_kwargs(**kwargs),
1703+
)
1704+
return LTIResourceLink(self._requester, response.json())
1705+
1706+
def get_lti_resource_links(self, **kwargs):
1707+
"""
1708+
Returns all LTI resource links for this course as a PaginatedList.
1709+
1710+
:calls: `GET /api/v1/courses/:course_id/lti_resource_links \
1711+
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.index>`_
1712+
1713+
:rtype: :class:`canvasapi.paginated_list.PaginatedList` of
1714+
:class:`canvasapi.lti_resource_link.LTIResourceLink`
1715+
"""
1716+
1717+
return PaginatedList(
1718+
LTIResourceLink,
1719+
self._requester,
1720+
"GET",
1721+
f"courses/{self.id}/lti_resource_links",
1722+
kwargs=combine_kwargs(**kwargs),
1723+
)
1724+
16481725
def get_migration_systems(self, **kwargs):
16491726
"""
16501727
Return a list of migration systems.

canvasapi/lti_resource_link.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from canvasapi.canvas_object import CanvasObject
2+
3+
4+
class LTIResourceLink(CanvasObject):
5+
def __str__(self):
6+
return "{} ({})".format(self.url, self.title)

docs/class-reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Class Reference
4141
jwt-ref
4242
login-ref
4343
license-ref
44+
lti-resource-link-ref
4445
module-ref
4546
outcome-ref
4647
outcome-import-ref

docs/lti-resource-link-ref.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
===============
2+
LTIResourceLink
3+
===============
4+
5+
.. autoclass:: canvasapi.lti_resource_link.LTIResourceLink
6+
:members:

tests/fixtures/lti_resource_link.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"create_lti_resource_link": {
3+
"method": "POST",
4+
"endpoint": "courses/1/lti_resource_links",
5+
"data": {
6+
"id": 45,
7+
"context_id": 1,
8+
"context_type": "Course",
9+
"context_external_tool_id": 1,
10+
"resource_type": "assignment",
11+
"canvas_launch_url": "https://example.instructure.com/courses/1/external_tools/retrieve?resource_link_lookup_uuid=ae43ba23-d238-49bc-ab55-ba7f79f77896",
12+
"resource_link_uuid": "ae43ba23-d238-49bc-ab55-ba7f79f77896",
13+
"lookup_uuid": "c522554a-d4be-49ef-b163-9c87fdc6ad6f",
14+
"title": "Test LTI Resource Link",
15+
"url": "https://example.com/lti/launch/content_item/123"
16+
},
17+
"status_code": 200
18+
},
19+
20+
"get_lti_resource_link": {
21+
"method": "GET",
22+
"endpoint": "courses/1/lti_resource_links/45",
23+
"data": {
24+
"id": 45,
25+
"title": "Test LTI Resource Link",
26+
"url": "https://example.com/lti/launch/content_item/123"
27+
},
28+
"status_code": 200
29+
},
30+
"list_lti_resource_links": {
31+
"method": "GET",
32+
"endpoint": "courses/1/lti_resource_links",
33+
"data": [
34+
{
35+
"id": 45,
36+
"title": "Test LTI Resource Link"
37+
},
38+
{
39+
"id": 56,
40+
"title": "Test LTI Resource Link 2"
41+
},
42+
{
43+
"id": 67,
44+
"title": "Test LTI Resource Link 3"
45+
}
46+
],
47+
"status_code": 200
48+
}
49+
}

tests/test_course.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from canvasapi.grading_standard import GradingStandard
3636
from canvasapi.group import Group, GroupCategory
3737
from canvasapi.license import License
38+
from canvasapi.lti_resource_link import LTIResourceLink
3839
from canvasapi.module import Module
3940
from canvasapi.new_quiz import NewQuiz
4041
from canvasapi.outcome import OutcomeGroup, OutcomeLink, OutcomeResult
@@ -1890,6 +1891,48 @@ def test_resolve_path_null(self, m):
18901891
self.assertIsInstance(root_folder_list[0], Folder)
18911892
self.assertEqual("course_files", root_folder_list[0].name)
18921893

1894+
# create_lti_resource_link()
1895+
def test_create_lti_resource_link(self, m):
1896+
register_uris({"lti_resource_link": ["create_lti_resource_link"]}, m)
1897+
custom_dict = {"hello": "world"}
1898+
1899+
evnt = self.course.create_lti_resource_link(
1900+
url="https://example.com/lti/launch/content_item/123",
1901+
title="Test LTI Resource Link",
1902+
custom=custom_dict,
1903+
)
1904+
self.assertIsInstance(evnt, LTIResourceLink)
1905+
1906+
self.assertEqual(evnt.title, "Test LTI Resource Link")
1907+
self.assertEqual(evnt.url, "https://example.com/lti/launch/content_item/123")
1908+
1909+
def test_create_lti_resource_link_fail(self, m):
1910+
with self.assertRaises(RequiredFieldMissing):
1911+
self.course.create_lti_resource_link({})
1912+
1913+
# get_lti_resource_links()
1914+
def test_get_lti_resource_links(self, m):
1915+
register_uris({"lti_resource_link": ["list_lti_resource_links"]}, m)
1916+
1917+
lti_resource_links = self.course.get_lti_resource_links()
1918+
lti_resource_link_list = [link for link in lti_resource_links]
1919+
self.assertEqual(len(lti_resource_link_list), 3)
1920+
self.assertIsInstance(lti_resource_link_list[0], LTIResourceLink)
1921+
1922+
# get_lti_resource_link()
1923+
def test_get_lti_resource_link(self, m):
1924+
register_uris({"lti_resource_link": ["get_lti_resource_link"]}, m)
1925+
1926+
lti_resource_link_by_id = self.course.get_lti_resource_link(45)
1927+
1928+
self.assertIsInstance(lti_resource_link_by_id, LTIResourceLink)
1929+
self.assertEqual(lti_resource_link_by_id.title, "Test LTI Resource Link")
1930+
lti_resource_link_by_obj = self.course.get_lti_resource_link(
1931+
lti_resource_link_by_id
1932+
)
1933+
self.assertIsInstance(lti_resource_link_by_obj, LTIResourceLink)
1934+
self.assertEqual(lti_resource_link_by_obj.title, "Test LTI Resource Link")
1935+
18931936

18941937
@requests_mock.Mocker()
18951938
class TestCourseNickname(unittest.TestCase):

tests/test_lti_resource_link.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import unittest
2+
3+
import requests_mock
4+
5+
from canvasapi import Canvas
6+
from tests import settings
7+
from tests.util import register_uris
8+
9+
10+
@requests_mock.Mocker()
11+
class TestLTIResourceLink(unittest.TestCase):
12+
def setUp(self):
13+
self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
14+
15+
with requests_mock.Mocker() as m:
16+
register_uris(
17+
{
18+
"course": ["get_by_id"],
19+
"lti_resource_link": ["get_lti_resource_link"],
20+
},
21+
m,
22+
)
23+
24+
self.course = self.canvas.get_course(1)
25+
self.resource_link = self.course.get_lti_resource_link(45)
26+
27+
# __str__()
28+
def test__str__(self, m):
29+
string = str(self.resource_link)
30+
self.assertIsInstance(string, str)

0 commit comments

Comments
 (0)