Skip to content

Commit

Permalink
Merge pull request #673 from jsmnhou/issue/662-api-lti-resource-links
Browse files Browse the repository at this point in the history
Issue/662 Add API coverage for LTI resource links
  • Loading branch information
Thetwam authored Dec 9, 2024
2 parents 5e4f260 + e001286 commit e9154d2
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
- Ian Altgilbers [@altgilbers](https://github.com/altgilbers)
- Ian Turgeon [@iturgeon](https://github.com/iturgeon)
- [@jackrsteiner](https://github.com/jackrsteiner)
- Jasmine Hou [@jsmnhou](https://github.com/jsmnhou)
- John Raible [@rebelaide](https://github.com/rebelaide)
- Joon Ro [@joonro](https://github.com/joonro)
- Jonah Majumder [@jonahmajumder](https://github.com/jonahmajumder)
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### New Endpoint Coverage

- LTI Resource Links (Thanks, [@jsmnhou](https://github.com/jsmnhou))

### Backstage

- Updated deploy Action to use more modern processes.
Expand Down
77 changes: 77 additions & 0 deletions canvasapi/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from canvasapi.grading_period import GradingPeriod
from canvasapi.grading_standard import GradingStandard
from canvasapi.license import License
from canvasapi.lti_resource_link import LTIResourceLink
from canvasapi.module import Module
from canvasapi.new_quiz import NewQuiz
from canvasapi.outcome_import import OutcomeImport
Expand Down Expand Up @@ -438,6 +439,39 @@ def create_late_policy(self, **kwargs):

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

def create_lti_resource_link(self, url, title=None, custom=None, **kwargs):
"""
Create a new LTI resource link.
:calls: `POST /api/v1/courses/:course_id/lti_resource_links \
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.create>`_
:param url: The launch URL for the resource link.
:type url: `str`
:param title: The title of the resource link.
:type title: `str`, optional
:param custom: Custom parameters to send to the tool.
:type custom: `dict`, optional
:rtype: :class:`canvasapi.lti_resource_link.LTIResourceLink`
"""

if not url:
raise RequiredFieldMissing("url is required as a str.")

kwargs["url"] = url
if title:
kwargs["title"] = title
if custom:
kwargs["custom"] = custom

response = self._requester.request(
"POST",
f"courses/{self.id}/lti_resource_links",
_kwargs=combine_kwargs(**kwargs),
)
return LTIResourceLink(self._requester, response.json())

def create_module(self, module, **kwargs):
"""
Create a new module.
Expand Down Expand Up @@ -1645,6 +1679,49 @@ def get_licenses(self, **kwargs):
_kwargs=combine_kwargs(**kwargs),
)

def get_lti_resource_link(self, lti_resource_link, **kwargs):
"""
Return details about the specified resource link.
:calls: `GET /api/v1/courses/:course_id/lti_resource_links/:id \
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.show>`_
:param lti_resource_link: The object or ID of the LTI resource link.
:type lti_resource_link: :class:`canvasapi.lti_resource_link.LTIResourceLink` or int
:rtype: :class:`canvasapi.lti_resource_link.LTIResourceLink`
"""

lti_resource_link_id = obj_or_id(
lti_resource_link, "lti_resource_link", (LTIResourceLink,)
)

response = self._requester.request(
"GET",
f"courses/{self.id}/lti_resource_links/{lti_resource_link_id}",
_kwargs=combine_kwargs(**kwargs),
)
return LTIResourceLink(self._requester, response.json())

def get_lti_resource_links(self, **kwargs):
"""
Returns all LTI resource links for this course as a PaginatedList.
:calls: `GET /api/v1/courses/:course_id/lti_resource_links \
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.index>`_
:rtype: :class:`canvasapi.paginated_list.PaginatedList` of
:class:`canvasapi.lti_resource_link.LTIResourceLink`
"""

return PaginatedList(
LTIResourceLink,
self._requester,
"GET",
f"courses/{self.id}/lti_resource_links",
kwargs=combine_kwargs(**kwargs),
)

def get_migration_systems(self, **kwargs):
"""
Return a list of migration systems.
Expand Down
6 changes: 6 additions & 0 deletions canvasapi/lti_resource_link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from canvasapi.canvas_object import CanvasObject


class LTIResourceLink(CanvasObject):
def __str__(self):
return "{} ({})".format(self.url, self.title)
1 change: 1 addition & 0 deletions docs/class-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Class Reference
jwt-ref
login-ref
license-ref
lti-resource-link-ref
module-ref
outcome-ref
outcome-import-ref
Expand Down
6 changes: 6 additions & 0 deletions docs/lti-resource-link-ref.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
===============
LTIResourceLink
===============

.. autoclass:: canvasapi.lti_resource_link.LTIResourceLink
:members:
49 changes: 49 additions & 0 deletions tests/fixtures/lti_resource_link.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"create_lti_resource_link": {
"method": "POST",
"endpoint": "courses/1/lti_resource_links",
"data": {
"id": 45,
"context_id": 1,
"context_type": "Course",
"context_external_tool_id": 1,
"resource_type": "assignment",
"canvas_launch_url": "https://example.instructure.com/courses/1/external_tools/retrieve?resource_link_lookup_uuid=ae43ba23-d238-49bc-ab55-ba7f79f77896",
"resource_link_uuid": "ae43ba23-d238-49bc-ab55-ba7f79f77896",
"lookup_uuid": "c522554a-d4be-49ef-b163-9c87fdc6ad6f",
"title": "Test LTI Resource Link",
"url": "https://example.com/lti/launch/content_item/123"
},
"status_code": 200
},

"get_lti_resource_link": {
"method": "GET",
"endpoint": "courses/1/lti_resource_links/45",
"data": {
"id": 45,
"title": "Test LTI Resource Link",
"url": "https://example.com/lti/launch/content_item/123"
},
"status_code": 200
},
"list_lti_resource_links": {
"method": "GET",
"endpoint": "courses/1/lti_resource_links",
"data": [
{
"id": 45,
"title": "Test LTI Resource Link"
},
{
"id": 56,
"title": "Test LTI Resource Link 2"
},
{
"id": 67,
"title": "Test LTI Resource Link 3"
}
],
"status_code": 200
}
}
43 changes: 43 additions & 0 deletions tests/test_course.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from canvasapi.grading_standard import GradingStandard
from canvasapi.group import Group, GroupCategory
from canvasapi.license import License
from canvasapi.lti_resource_link import LTIResourceLink
from canvasapi.module import Module
from canvasapi.new_quiz import NewQuiz
from canvasapi.outcome import OutcomeGroup, OutcomeLink, OutcomeResult
Expand Down Expand Up @@ -1890,6 +1891,48 @@ def test_resolve_path_null(self, m):
self.assertIsInstance(root_folder_list[0], Folder)
self.assertEqual("course_files", root_folder_list[0].name)

# create_lti_resource_link()
def test_create_lti_resource_link(self, m):
register_uris({"lti_resource_link": ["create_lti_resource_link"]}, m)
custom_dict = {"hello": "world"}

evnt = self.course.create_lti_resource_link(
url="https://example.com/lti/launch/content_item/123",
title="Test LTI Resource Link",
custom=custom_dict,
)
self.assertIsInstance(evnt, LTIResourceLink)

self.assertEqual(evnt.title, "Test LTI Resource Link")
self.assertEqual(evnt.url, "https://example.com/lti/launch/content_item/123")

def test_create_lti_resource_link_fail(self, m):
with self.assertRaises(RequiredFieldMissing):
self.course.create_lti_resource_link({})

# get_lti_resource_links()
def test_get_lti_resource_links(self, m):
register_uris({"lti_resource_link": ["list_lti_resource_links"]}, m)

lti_resource_links = self.course.get_lti_resource_links()
lti_resource_link_list = [link for link in lti_resource_links]
self.assertEqual(len(lti_resource_link_list), 3)
self.assertIsInstance(lti_resource_link_list[0], LTIResourceLink)

# get_lti_resource_link()
def test_get_lti_resource_link(self, m):
register_uris({"lti_resource_link": ["get_lti_resource_link"]}, m)

lti_resource_link_by_id = self.course.get_lti_resource_link(45)

self.assertIsInstance(lti_resource_link_by_id, LTIResourceLink)
self.assertEqual(lti_resource_link_by_id.title, "Test LTI Resource Link")
lti_resource_link_by_obj = self.course.get_lti_resource_link(
lti_resource_link_by_id
)
self.assertIsInstance(lti_resource_link_by_obj, LTIResourceLink)
self.assertEqual(lti_resource_link_by_obj.title, "Test LTI Resource Link")


@requests_mock.Mocker()
class TestCourseNickname(unittest.TestCase):
Expand Down
30 changes: 30 additions & 0 deletions tests/test_lti_resource_link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import unittest

import requests_mock

from canvasapi import Canvas
from tests import settings
from tests.util import register_uris


@requests_mock.Mocker()
class TestLTIResourceLink(unittest.TestCase):
def setUp(self):
self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)

with requests_mock.Mocker() as m:
register_uris(
{
"course": ["get_by_id"],
"lti_resource_link": ["get_lti_resource_link"],
},
m,
)

self.course = self.canvas.get_course(1)
self.resource_link = self.course.get_lti_resource_link(45)

# __str__()
def test__str__(self, m):
string = str(self.resource_link)
self.assertIsInstance(string, str)

0 comments on commit e9154d2

Please sign in to comment.