Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/lib/atomic_lti/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def self.client_assertion(iss:, deployment_id:)
raise AtomicLti::Exceptions::NoLTIPlatform.new(iss: iss, deployment_id: deployment_id) if platform.nil?

payload = {
iss: install.client_id, # A unique identifier for the entity that issued the JWT
iss: install.client_id, # A unique identifier for the entity that issued the JWT
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layout/ExtraSpacing: Unnecessary spacing detected.

sub: install.client_id, # "client_id" of the OAuth Client
aud: platform.token_url, # Authorization server identifier
iat: Time.now.to_i, # Timestamp for when the JWT was created
Expand Down
7 changes: 6 additions & 1 deletion app/lib/atomic_lti/definitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class Definitions
DEEP_LINKING_DATA_CLAIM = "https://purl.imsglobal.org/spec/lti-dl/claim/data".freeze
DEEP_LINKING_TOOL_MSG_CLAIM = "https://purl.imsglobal.org/spec/lti-dl/claim/msg".freeze
DEEP_LINKING_TOOL_LOG_CLAIM = "https://purl.imsglobal.org/spec/lti-dl/claim/log".freeze
DEEP_LINKING_SERVICE_CLAIM = "https://purl.imsglobal.org/spec/lti-dl/claim/deeplinkingservice".freeze
CONTENT_ITEM_CLAIM = "https://purl.imsglobal.org/spec/lti-dl/claim/content_items".freeze
NAMES_AND_ROLES_CLAIM = "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice".freeze

NAMES_AND_ROLES_SERVICE_VERSIONS = ["2.0"].freeze

CALIPER_CLAIM = "https://purl.imsglobal.org/spec/lti-ces/claim/caliper-endpoint-service".freeze
Expand All @@ -43,6 +43,11 @@ class Definitions
NAMES_AND_ROLES_SCOPE = "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly".freeze
CALIPER_SCOPE = "https://purl.imsglobal.org/spec/lti-ces/v1p0/scope/send".freeze

DEEP_LINKING_READ = "https://purl.imsglobal.org/spec/lti-dl/scope/contentitem.read".freeze
DEEP_LINKING_UPDATE = "https://purl.imsglobal.org/spec/lti-dl/scope/contentitem.update".freeze
DEEP_LINKING_ADD = "https://purl.imsglobal.org/spec/lti-dl/scope/contentitem.add".freeze
DEEP_LINKING_DELETE = "https://purl.imsglobal.org/spec/lti-dl/scope/contentitem.delete".freeze

STUDENT_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student".freeze
INSTRUCTOR_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor".freeze
LEARNER_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/membership#Learner".freeze
Expand Down
121 changes: 121 additions & 0 deletions app/lib/atomic_lti/services/deep_linking.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Implemenation of
# https://github.com/1EdTech/LTI-central/blob/main/proposals/deeplinking-service/specification.md#deep-linking-rest-service-1
module AtomicLti
module Services
class DeepLinking < AtomicLti::Services::Base

def initialize(id_token_decoded:)
super(id_token_decoded: id_token_decoded)
end

def scopes
@id_token_decoded&.dig(AtomicLti::Definitions::DEEP_LINKING_SERVICE_CLAIM, "scopes")
end

def content_items_endpoint
url = @id_token_decoded.dig(AtomicLti::Definitions::DEEP_LINKING_SERVICE_CLAIM, "contentitems")
raise AtomicLti::Exceptions::DEEP_LINKING_SERVICE_CLAIM, "Unable to access content items url" if url.blank?

url
end

def content_item_endpoint
url = @id_token_decoded.dig(AtomicLti::Definitions::DEEP_LINKING_SERVICE_CLAIM, "contentitem")
raise AtomicLti::Exceptions::DEEP_LINKING_SERVICE_CLAIM, "Unable to access content item url" if url.blank?

url
end

# Allows a tool to get a list of all items linked to the tool in a given context.
# Parameters:
# query may include:
# limit: Specifies the maximum number of results to return. The platform may return fewer results.
# rlid: Filters results to the specified resource link id.
# See: https://github.com/1EdTech/LTI-central/blob/main/proposals/deeplinking-service/specification.md#filter-by-resourcelinkid
# Returns:
# An array of items as JSON.
# Example:
# "items": [
# {
# "readonly": ["available", "resourceLinkId", "id"],
# "id": "https://www.myuniv.example.com/2344/content_items/389a-5478-78fg",
# "type": "ltiResourceLink",
# "resourceLinkId": "389a-5478-78fg",
# "title": "A title",
# "text": "This is a link to an activity that will be graded",
# "url": "https://lti.example.com/launchMe",
# "icon": {
# "url": "https://lti.example.com/image.jpg",
# "width": 100,
# "height": 100
# },
# "thumbnail": {
# "url": "https://lti.example.com/thumb.jpg",
# "width": 90,
# "height": 90
# },
# "lineItemId": "https://www.myuniv.example.com/2344/lineitems/345991",
# "available": {
# "startDateTime": "2024-02-06T20:05:02Z",
# "endDateTime": "2024-03-07T20:05:02Z"
# },
# "submission": {
# "endDateTime": "2024-03-06T20:05:02Z"
# },
# "custom": {
# "quiz_id": "az-123",
# "duedate": "$Resource.submission.endDateTime"
# }
# }
# ]
# }
def list(query = {})
accept = { "Accept" => "application/vnd.1edtech.lti.contentitems+json" }
HTTParty.get(content_items_endpoint, headers: headers(accept), query: query)
end

# Updates the content item
#
# Parameters:
# content_item_url: The url of the content item to update
# content_item: The content item to update
# The id, lineItemId, resourceLinkId and type properties are inherently read-only.
#
# Example content_item:
# {
# "id": "https://www.myuniv.example.com/2344/content_items/389a-5478-4712",
# "type": "ltiResourceLink",
# "resourceLinkId": "389a-5478-4712",
# "title": "The ghost of the republic updated",
# "text": "This is a link to a video resource",
# "url": "https://lti.example.com/launchMe",
# "custom": {
# "video_id": "89042-ejxl01-updated",
# }
# "available": {
# "startDateTime": "2024-02-06T20:05:02Z",
# "endDateTime": "2024-03-11T22:00:00Z"
# },
# "submission": {
# "endDateTime": "2024-03-08T22:00:00Z"
# }
# }
#
# Returns:
# The updated content item as JSON.
def update(content_item_url, content_item)
accept = { "Accept" => "application/vnd.1edtech.lti.contentitem+json" }
HTTParty.put(content_item_url, headers: headers(accept), body: content_item.to_json)
end

# Adds a new content item. This is an optional method since platforms are not required to support an add endpoint.
# Check for platform support before calling add.
# If a line item was requested, and the platform created it, the lineItemId must be included in the response.
def add(content_item)
accept = { "Accept" => "application/vnd.1edtech.lti.contentitem+json" }
HTTParty.post(content_items_endpoint, headers: headers(accept), body: content_item.to_json)
end

end
end
end