WIP basic_lti_outbound gem extraction
Change-Id: Icb8f78bdd5f41e4eae18b30d1a4cfc7dd9942057 Reviewed-on: https://gerrit.instructure.com/28722 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Raphael Weiner <rweiner@pivotallabs.com> Product-Review: Brad Humphrey <brad@instructure.com> QA-Review: Brad Humphrey <brad@instructure.com>
This commit is contained in:
parent
701204a560
commit
ea7e45c793
|
@ -0,0 +1,17 @@
|
|||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
InstalledFiles
|
||||
_yardoc
|
||||
coverage
|
||||
doc/
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
|
@ -0,0 +1,2 @@
|
|||
--color
|
||||
--format progress
|
|
@ -0,0 +1,4 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in basic_lti_outbound.gemspec
|
||||
gemspec
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2014 Brian Palmer
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,29 @@
|
|||
# BasicLtiOutbound
|
||||
|
||||
TODO: Write a gem description
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
gem 'basic_lti'
|
||||
|
||||
And then execute:
|
||||
|
||||
$ bundle
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
$ gem install basic_lti
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Write usage instructions here
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it ( http://github.com/<my-github-username>/basic_lti/fork )
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
|
@ -0,0 +1 @@
|
|||
require "bundler/gem_tasks"
|
|
@ -0,0 +1,25 @@
|
|||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "basic_lti_outbound"
|
||||
spec.version = "0.0.1"
|
||||
spec.authors = ["Brian Palmer"]
|
||||
spec.email = ["brianp@instructure.com"]
|
||||
spec.summary = %q{LTI consumer service}
|
||||
spec.homepage = "https://github.com/instructure/canvas-lms"
|
||||
spec.license = "AGPL"
|
||||
|
||||
spec.files = `git ls-files`.split($/)
|
||||
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_dependency "i18n", "0.6.8"
|
||||
spec.add_dependency "oauth-instructure", "0.4.9"
|
||||
|
||||
spec.add_development_dependency "bundler", "~> 1.5"
|
||||
spec.add_development_dependency "rake"
|
||||
spec.add_development_dependency "rspec"
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
require 'uri'
|
||||
require 'oauth'
|
||||
require 'oauth/consumer'
|
||||
require "i18n"
|
||||
|
||||
module BasicLtiOutbound
|
||||
require "basic_lti_outbound/lti_tool"
|
||||
require "basic_lti_outbound/lti_model"
|
||||
require "basic_lti_outbound/lti_context"
|
||||
require "basic_lti_outbound/lti_account"
|
||||
require "basic_lti_outbound/lti_course"
|
||||
require "basic_lti_outbound/lti_role"
|
||||
require "basic_lti_outbound/lti_user"
|
||||
require "basic_lti_outbound/lti_assignment"
|
||||
require "basic_lti_outbound/tool_launch"
|
||||
require "basic_lti_outbound/variable_substitutor"
|
||||
|
||||
def self.generate(*args)
|
||||
BasicLtiOutbound::ToolLaunch.new(*args).generate
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
module BasicLtiOutbound
|
||||
class LTIAccount < LTIContext
|
||||
attr_accessor :lti_guid, :name, :domain
|
||||
|
||||
add_variable_mapping ".domain", :domain
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
module BasicLtiOutbound
|
||||
class LTIAssignment < LTIContext
|
||||
attr_accessor :id, :source_id, :title, :points_possible, :return_types, :allowed_extensions
|
||||
|
||||
add_variable_mapping ".id", :id
|
||||
add_variable_mapping ".title", :title
|
||||
add_variable_mapping ".pointsPossible", :points_possible
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
module BasicLtiOutbound
|
||||
class LTIContext < LTIModel
|
||||
attr_accessor :root_account, :opaque_identifier, :id, :sis_source_id
|
||||
|
||||
add_variable_mapping ".id", :id
|
||||
add_variable_mapping ".sisSourceId", :sis_source_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
module BasicLtiOutbound
|
||||
class LTICourse < LTIContext
|
||||
attr_accessor :course_code, :name
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module BasicLtiOutbound
|
||||
class LTIModel
|
||||
def variable_substitution_mapping(placeholder)
|
||||
@@substititions ||= {}
|
||||
@@substititions[placeholder] && send(@@substititions[placeholder])
|
||||
end
|
||||
|
||||
def has_variable_mapping?(placeholder)
|
||||
@@substititions ||= {}
|
||||
!!@@substititions[placeholder]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.add_variable_mapping(placeholder, substitution_method)
|
||||
@@substititions ||= {}
|
||||
@@substititions[placeholder] = substitution_method
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
module BasicLtiOutbound
|
||||
class LTIRole
|
||||
INSTRUCTOR = "Instructor"
|
||||
LEARNER = "Learner"
|
||||
ADMIN = "urn:lti:instrole:ims/lis/Administrator"
|
||||
CONTENT_DEVLOPER = "ContentDeveloper"
|
||||
OBSERVER = "urn:lti:instrole:ims/lis/Observer"
|
||||
TEACHING_ASSISTANT = "urn:lti:role:ims/lis/TeachingAssistant"
|
||||
NONE = "urn:lti:sysrole:ims/lis/None"
|
||||
|
||||
|
||||
|
||||
#def self.enrollment_to_membership(membership)
|
||||
# case membership
|
||||
# when StudentEnrollment, StudentViewEnrollment
|
||||
# 'Learner'
|
||||
# when TeacherEnrollment
|
||||
# 'Instructor'
|
||||
# when TaEnrollment
|
||||
# 'urn:lti:role:ims/lis/TeachingAssistant'
|
||||
# when DesignerEnrollment
|
||||
# 'ContentDeveloper'
|
||||
# when ObserverEnrollment
|
||||
# 'urn:lti:instrole:ims/lis/Observer'
|
||||
# when AccountUser
|
||||
# 'urn:lti:instrole:ims/lis/Administrator'
|
||||
# else
|
||||
# 'urn:lti:instrole:ims/lis/Observer'
|
||||
# end
|
||||
#end
|
||||
|
||||
attr_accessor :type, :state
|
||||
|
||||
def active?
|
||||
@state == :active
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,76 @@
|
|||
module BasicLtiOutbound
|
||||
class LTITool
|
||||
PRIVACY_LEVEL_PUBLIC = :public
|
||||
PRIVACY_LEVEL_NAME_ONLY = :name_only
|
||||
PRIVACY_LEVEL_EMAIL_ONLY = :email_only
|
||||
PRIVACY_LEVEL_ANYNOMOUS = :anonymous
|
||||
|
||||
attr_accessor :consumer_key, :context, :privacy_level, :name, :settings, :shared_secret
|
||||
|
||||
def include_name?
|
||||
[PRIVACY_LEVEL_PUBLIC, PRIVACY_LEVEL_NAME_ONLY].include? privacy_level
|
||||
end
|
||||
|
||||
def include_email?
|
||||
[PRIVACY_LEVEL_PUBLIC, PRIVACY_LEVEL_EMAIL_ONLY].include? privacy_level
|
||||
end
|
||||
|
||||
def public?
|
||||
[PRIVACY_LEVEL_PUBLIC].include? privacy_level
|
||||
end
|
||||
|
||||
def settings
|
||||
@settings || {}
|
||||
end
|
||||
|
||||
# sets the custom fields from the main tool settings, and any on individual resource type settings
|
||||
def set_custom_fields(hash, resource_type)
|
||||
fields = [settings[:custom_fields] || {}]
|
||||
if resource_type && settings[resource_type.to_sym]
|
||||
fields << (settings[resource_type.to_sym][:custom_fields] || {})
|
||||
end
|
||||
fields.each do |field_set|
|
||||
field_set.each do |key, val|
|
||||
key = key.to_s.gsub(/[^\w]/, '_').downcase
|
||||
if key.match(/^custom_/)
|
||||
hash[key] = val
|
||||
else
|
||||
hash["custom_#{key}"] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def selection_width(resource_type)
|
||||
extension_setting(resource_type, :selection_width)
|
||||
end
|
||||
|
||||
def selection_height(resource_type)
|
||||
extension_setting(resource_type, :selection_height)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#Duplicated in ContextExternalTool
|
||||
def extension_setting(type, property = nil)
|
||||
type = type.to_sym
|
||||
return settings[type] unless property
|
||||
(settings[type] && settings[type][property]) || settings[property] || extension_default_value(property)
|
||||
end
|
||||
|
||||
#Duplicated in ContextExternalTool
|
||||
def extension_default_value(property)
|
||||
case property
|
||||
when :url
|
||||
url
|
||||
when :selection_width
|
||||
800
|
||||
when :selection_height
|
||||
400
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
module BasicLtiOutbound
|
||||
class LTIUser < LTIContext
|
||||
ACTIVE_STATE = 'active'
|
||||
INACTIVE_STATE = 'inactive'
|
||||
|
||||
attr_accessor :avatar_url, :email, :login_id, :first_name, :last_name, :name,
|
||||
:current_enrollments, :concluded_enrollments,
|
||||
:sis_user_id
|
||||
|
||||
add_variable_mapping ".login_id", :login_id
|
||||
add_variable_mapping ".enrollment_state", :enrollment_state
|
||||
add_variable_mapping ".concluded_roles", :concluded_roles
|
||||
add_variable_mapping ".full", :full_name
|
||||
add_variable_mapping ".family", :family_name
|
||||
add_variable_mapping ".given", :given_name
|
||||
add_variable_mapping ".timezone", :timezone
|
||||
|
||||
def current_role_types
|
||||
roles = current_enrollments.map(&:type).join(',') if current_enrollments && current_enrollments.size > 0
|
||||
roles || BasicLtiOutbound::LTIRole::NONE
|
||||
end
|
||||
|
||||
def concluded_role_types
|
||||
roles = concluded_enrollments.map(&:type).join(',') if concluded_enrollments && concluded_enrollments.size > 0
|
||||
roles || BasicLtiOutbound::LTIRole::NONE
|
||||
end
|
||||
|
||||
def enrollment_state
|
||||
current_enrollments.any? { |e| e.active? } ? BasicLtiOutbound::LTIUser::ACTIVE_STATE : BasicLtiOutbound::LTIUser::INACTIVE_STATE
|
||||
end
|
||||
|
||||
def learner?
|
||||
current_enrollments.any? { |e| e.type == BasicLtiOutbound::LTIRole::LEARNER }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,203 @@
|
|||
#
|
||||
# Copyright (C) 2013 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
module BasicLtiOutbound
|
||||
class ToolLaunch
|
||||
attr_reader :url, :tool, :user, :context, :link_code, :return_url,
|
||||
:resource_type, :root_account, :hash, :assignment, :outgoing_email_address
|
||||
|
||||
def initialize(options)
|
||||
@url = options[:url] || raise("URL required for generating Basic LTI content")
|
||||
@tool = options[:tool] || raise("Tool required for generating Basic LTI content")
|
||||
@user = options[:user] || raise("User required for generating Basic LTI content")
|
||||
@context = options[:context] || raise("Context required for generating Basic LTI content")
|
||||
@link_code = options[:link_code] || raise("Link Code required for generating Basic LTI content")
|
||||
@return_url = options[:return_url] || raise("Return URL required for generating Basic LTI content")
|
||||
@resource_type = options[:resource_type]
|
||||
@outgoing_email_address = options[:outgoing_email_address]
|
||||
@root_account = context.root_account || tool.context.root_account || raise("Root account required for generating Basic LTI content")
|
||||
|
||||
@hash = {}
|
||||
end
|
||||
|
||||
#def pseudonym
|
||||
# @pseudonym ||= user.find_pseudonym_for_account(root_account)
|
||||
#end
|
||||
|
||||
def for_assignment!(assignment, outcome_service_url, legacy_outcome_service_url)
|
||||
@assignment = assignment
|
||||
hash['lis_result_sourcedid'] = assignment.source_id if user.learner?
|
||||
hash['lis_outcome_service_url'] = outcome_service_url
|
||||
hash['ext_ims_lis_basic_outcome_url'] = legacy_outcome_service_url
|
||||
hash['ext_outcome_data_values_accepted'] = assignment.return_types.join(',')
|
||||
hash['custom_canvas_assignment_title'] = '$Canvas.assignment.title'
|
||||
hash['custom_canvas_assignment_points_possible'] = '$Canvas.assignment.pointsPossible'
|
||||
if tool.public?
|
||||
hash['custom_canvas_assignment_id'] = '$Canvas.assignment.id'
|
||||
end
|
||||
end
|
||||
|
||||
def for_homework_submission!(assignment)
|
||||
@assignment = assignment
|
||||
@resource_type = 'homework_submission'
|
||||
|
||||
hash['ext_content_return_types'] = assignment.return_types.join(',')
|
||||
hash['ext_content_file_extensions'] = assignment.allowed_extensions.join(',') if assignment.allowed_extensions
|
||||
|
||||
hash['custom_canvas_assignment_id'] = '$Canvas.assignment.id'
|
||||
end
|
||||
|
||||
def has_selection_html!(html)
|
||||
hash['text'] = CGI::escape(html)
|
||||
end
|
||||
|
||||
def generate
|
||||
hash['lti_message_type'] = 'basic-lti-launch-request'
|
||||
hash['lti_version'] = 'LTI-1p0'
|
||||
hash['resource_link_id'] = link_code
|
||||
hash['resource_link_title'] = tool.name
|
||||
hash['user_id'] = user.opaque_identifier
|
||||
hash['user_image'] = user.avatar_url
|
||||
|
||||
hash['roles'] = user.current_role_types # AccountAdmin, Student, Faculty or Observer
|
||||
|
||||
hash['custom_canvas_enrollment_state'] = '$Canvas.enrollment.enrollmentState'
|
||||
|
||||
if tool.include_name?
|
||||
hash['lis_person_name_given'] = user.first_name
|
||||
hash['lis_person_name_family'] = user.last_name
|
||||
hash['lis_person_name_full'] = user.name
|
||||
end
|
||||
if tool.include_email?
|
||||
hash['lis_person_contact_email_primary'] = user.email
|
||||
end
|
||||
if tool.public?
|
||||
hash['custom_canvas_user_id'] = '$Canvas.user.id'
|
||||
hash['lis_person_sourcedid'] = user.sis_user_id if user.sis_user_id
|
||||
hash['custom_canvas_user_login_id'] = '$Canvas.user.login_id'
|
||||
if context.is_a?(LTICourse)
|
||||
hash['custom_canvas_course_id'] = '$Canvas.context.id'
|
||||
hash['lis_course_offering_sourcedid'] = context.sis_source_id if context.sis_source_id
|
||||
elsif context.is_a?(LTIAccount) || context.is_a?(LTIUser)
|
||||
hash['custom_canvas_account_id'] = '$Canvas.account.id'
|
||||
hash['custom_canvas_account_sis_id'] = '$Canvas.account.sisSourceId'
|
||||
end
|
||||
hash['custom_canvas_api_domain'] = '$Canvas.api.domain'
|
||||
end
|
||||
|
||||
# need to set the locale here (instead of waiting for the first call to
|
||||
# I18n.t like we usually do), because otherwise we'll have the wrong code
|
||||
# for the launch_presentation_locale.
|
||||
I18n.set_locale_with_localizer
|
||||
|
||||
hash['context_id'] = context.opaque_identifier
|
||||
hash['context_title'] = context.name
|
||||
hash['context_label'] = context.course_code if context.respond_to?(:course_code)
|
||||
hash['launch_presentation_locale'] = I18n.locale || I18n.default_locale.to_s
|
||||
hash['launch_presentation_document_target'] = 'iframe'
|
||||
if resource_type
|
||||
hash['launch_presentation_width'] = tool.selection_width(resource_type)
|
||||
hash['launch_presentation_height'] = tool.selection_height(resource_type)
|
||||
end
|
||||
hash['launch_presentation_return_url'] = return_url
|
||||
hash['tool_consumer_instance_guid'] = root_account.lti_guid
|
||||
hash['tool_consumer_instance_name'] = root_account.name
|
||||
hash['tool_consumer_instance_contact_email'] = outgoing_email_address # TODO: find a better email address to use here
|
||||
hash['tool_consumer_info_product_family_code'] = 'canvas'
|
||||
hash['tool_consumer_info_version'] = 'cloud'
|
||||
tool.set_custom_fields(hash, resource_type)
|
||||
if resource_type == 'editor_button'
|
||||
hash['selection_directive'] = 'embed_content' #backwards compatibility
|
||||
hash['ext_content_intended_use'] = 'embed'
|
||||
hash['ext_content_return_types'] = 'oembed,lti_launch_url,url,image_url,iframe'
|
||||
hash['ext_content_return_url'] = return_url
|
||||
elsif resource_type == 'resource_selection'
|
||||
hash['selection_directive'] = 'select_link' #backwards compatibility
|
||||
hash['ext_content_intended_use'] = 'navigation'
|
||||
hash['ext_content_return_types'] = 'lti_launch_url'
|
||||
hash['ext_content_return_url'] = return_url
|
||||
elsif resource_type == 'homework_submission'
|
||||
hash['ext_content_intended_use'] = 'homework'
|
||||
hash['ext_content_return_url'] = return_url
|
||||
end
|
||||
hash['oauth_callback'] = 'about:blank'
|
||||
|
||||
variable_substitutor = VariableSubstitutor.new
|
||||
variable_substitutor.substitute!(hash, "$Canvas.user", user)
|
||||
variable_substitutor.substitute!(hash, "$Canvas.context", context)
|
||||
variable_substitutor.substitute!(hash, "$Canvas.api", root_account)
|
||||
variable_substitutor.substitute!(hash, "$Canvas.assignment", assignment) if assignment
|
||||
variable_substitutor.substitute!(hash, "$Canvas.account", context.is_a?(LTIAccount) ? context : root_account)
|
||||
variable_substitutor.substitute!(hash, "$Canvas.membership", user)
|
||||
variable_substitutor.substitute!(hash, "$Canvas.enrollment", user)
|
||||
variable_substitutor.substitute!(hash, "$Person.name", user)
|
||||
variable_substitutor.substitute!(hash, "$Person.address", user)
|
||||
|
||||
self.class.generate_params(hash, url, tool.consumer_key, tool.shared_secret)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.generate_params(params, url, key, secret)
|
||||
uri = URI.parse(url)
|
||||
|
||||
if uri.port == uri.default_port
|
||||
host = uri.host
|
||||
else
|
||||
host = "#{uri.host}:#{uri.port}"
|
||||
end
|
||||
|
||||
consumer = OAuth::Consumer.new(key, secret, {
|
||||
:site => "#{uri.scheme}://#{host}",
|
||||
:signature_method => "HMAC-SHA1"
|
||||
})
|
||||
|
||||
path = uri.path
|
||||
path = '/' if path.empty?
|
||||
if uri.query && uri.query != ""
|
||||
CGI.parse(uri.query).each do |query_key, query_values|
|
||||
unless params[query_key]
|
||||
params[query_key] = query_values.first
|
||||
end
|
||||
end
|
||||
end
|
||||
options = {
|
||||
:scheme => 'body',
|
||||
:timestamp => @timestamp,
|
||||
:nonce => @nonce
|
||||
}
|
||||
|
||||
request = consumer.create_signed_request(:post, path, nil, options, stringify_hash(params))
|
||||
|
||||
# the request is made by a html form in the user's browser, so we
|
||||
# want to revert the escapage and return the hash of post parameters ready
|
||||
# for embedding in a html view
|
||||
hash = {}
|
||||
request.body.split(/&/).each do |param|
|
||||
key, val = param.split(/=/).map{|v| CGI.unescape(v) }
|
||||
hash[key] = val
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
def self.stringify_hash(hash)
|
||||
hash.dup.tap do |new_hash|
|
||||
new_hash.keys.each { |k| new_hash[k.to_s] = new_hash.delete(k) unless k.is_a?(String) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# Copyright (C) 2013 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module BasicLtiOutbound
|
||||
class VariableSubstitutor
|
||||
#This method changes given data_hash!
|
||||
def substitute!(data_hash, substitution_base_name, substitution_object)
|
||||
data_hash.each do |key, val|
|
||||
if val.to_s.start_with?(substitution_base_name)
|
||||
attribute = val.gsub(substitution_base_name, "")
|
||||
if substitution_object.has_variable_mapping?(attribute)
|
||||
replacement_value = substitution_object.variable_substitution_mapping(attribute)
|
||||
data_hash[key] = replacement_value if replacement_value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTIAccount do
|
||||
it_behaves_like "an LTI context"
|
||||
|
||||
it_behaves_like "it has an attribute setter and getter for", :lti_guid
|
||||
it_behaves_like "it has an attribute setter and getter for", :name
|
||||
it_behaves_like "it has an attribute setter and getter for", :domain
|
||||
|
||||
it_behaves_like "it provides variable mapping", ".domain", :domain
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTIAssignment do
|
||||
it_behaves_like "it has an attribute setter and getter for", :id
|
||||
it_behaves_like "it has an attribute setter and getter for", :source_id
|
||||
it_behaves_like "it has an attribute setter and getter for", :title
|
||||
it_behaves_like "it has an attribute setter and getter for", :points_possible
|
||||
it_behaves_like "it has an attribute setter and getter for", :return_types
|
||||
it_behaves_like "it has an attribute setter and getter for", :allowed_extensions
|
||||
|
||||
it_behaves_like "it provides variable mapping", ".id", :id
|
||||
it_behaves_like "it provides variable mapping", ".title", :title
|
||||
it_behaves_like "it provides variable mapping", ".pointsPossible", :points_possible
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTIContext do
|
||||
it_behaves_like "an LTI context"
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTICourse do
|
||||
it_behaves_like "an LTI context"
|
||||
|
||||
it_behaves_like "it has an attribute setter and getter for", :course_code
|
||||
it_behaves_like "it has an attribute setter and getter for", :name
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTIModel do
|
||||
class Dummy < BasicLtiOutbound::LTIModel
|
||||
attr_accessor :test
|
||||
add_variable_mapping ".test", :test
|
||||
end
|
||||
|
||||
describe "#has_variable_mapping?" do
|
||||
it "returns true if mapping exists" do
|
||||
model = Dummy.new
|
||||
expect(model.has_variable_mapping?('.test')).to eq true
|
||||
end
|
||||
|
||||
it "returns false if mapping does not exist" do
|
||||
model = Dummy.new
|
||||
expect(model.has_variable_mapping?('.none')).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#variable_substitution_mapping" do
|
||||
it "returns nil for any variable_substitution_call" do
|
||||
model = BasicLtiOutbound::LTIModel.new
|
||||
expect(model.variable_substitution_mapping(:something)).to eq nil
|
||||
expect(model.variable_substitution_mapping(:something_else)).to eq nil
|
||||
expect(model.variable_substitution_mapping(nil)).to eq nil
|
||||
expect(model.variable_substitution_mapping([])).to eq nil
|
||||
end
|
||||
|
||||
it "calls the mapped method" do
|
||||
model = Dummy.new
|
||||
model.test = 'value'
|
||||
expect(model.variable_substitution_mapping('.test')).to eq 'value'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTIRole do
|
||||
it_behaves_like "it has an attribute setter and getter for", :type
|
||||
it_behaves_like "it has an attribute setter and getter for", :state
|
||||
|
||||
it "returns active? IFF the state is :active" do
|
||||
expect(subject.active?).to be false
|
||||
subject.state = "something irrelevant"
|
||||
expect(subject.active?).to be false
|
||||
subject.state = :active
|
||||
expect(subject.active?).to be true
|
||||
end
|
||||
|
||||
describe "constants" do
|
||||
it "provides role constants" do
|
||||
expect(BasicLtiOutbound::LTIRole::INSTRUCTOR).to eq "Instructor"
|
||||
expect(BasicLtiOutbound::LTIRole::LEARNER).to eq "Learner"
|
||||
expect(BasicLtiOutbound::LTIRole::ADMIN).to eq "urn:lti:instrole:ims/lis/Administrator"
|
||||
expect(BasicLtiOutbound::LTIRole::CONTENT_DEVLOPER).to eq "ContentDeveloper"
|
||||
expect(BasicLtiOutbound::LTIRole::OBSERVER).to eq "urn:lti:instrole:ims/lis/Observer"
|
||||
expect(BasicLtiOutbound::LTIRole::TEACHING_ASSISTANT).to eq "urn:lti:role:ims/lis/TeachingAssistant"
|
||||
expect(BasicLtiOutbound::LTIRole::NONE).to eq "urn:lti:sysrole:ims/lis/None"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,145 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTITool do
|
||||
it_behaves_like "it has an attribute setter and getter for", :consumer_key
|
||||
it_behaves_like "it has an attribute setter and getter for", :context
|
||||
it_behaves_like "it has an attribute setter and getter for", :privacy_level
|
||||
it_behaves_like "it has an attribute setter and getter for", :name
|
||||
it_behaves_like "it has an attribute setter and getter for", :shared_secret
|
||||
|
||||
describe "#include_name?" do
|
||||
it "returns true IFF the privacy level is public or name only" do
|
||||
subject.privacy_level = :something
|
||||
expect(subject.include_name?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_PUBLIC
|
||||
expect(subject.include_name?).to eq true
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_NAME_ONLY
|
||||
expect(subject.include_name?).to eq true
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_EMAIL_ONLY
|
||||
expect(subject.include_name?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_ANYNOMOUS
|
||||
expect(subject.include_name?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#include_email?" do
|
||||
it "returns true IFF the privacy level is public or email only" do
|
||||
subject.privacy_level = :something
|
||||
expect(subject.include_email?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_PUBLIC
|
||||
expect(subject.include_email?).to eq true
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_NAME_ONLY
|
||||
expect(subject.include_email?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_EMAIL_ONLY
|
||||
expect(subject.include_email?).to eq true
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_ANYNOMOUS
|
||||
expect(subject.include_email?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#public?" do
|
||||
it "returns true IFF the privacy level is public" do
|
||||
subject.privacy_level = :something
|
||||
expect(subject.public?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_PUBLIC
|
||||
expect(subject.public?).to eq true
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_NAME_ONLY
|
||||
expect(subject.public?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_EMAIL_ONLY
|
||||
expect(subject.public?).to eq false
|
||||
subject.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_ANYNOMOUS
|
||||
expect(subject.public?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
describe "#settings" do
|
||||
it "attribute setter, but returns {} instead of nil" do
|
||||
expect(subject.settings).to eq({})
|
||||
subject.settings = 10
|
||||
expect(subject.settings).to eq 10
|
||||
end
|
||||
end
|
||||
|
||||
describe "#set_custom_fields" do
|
||||
it "does not change the given input hash if the settings' custom fields are empty" do
|
||||
hash = {:a => :b}
|
||||
|
||||
subject.settings = {}
|
||||
subject.set_custom_fields(hash, nil)
|
||||
expect(hash).to eq({:a => :b})
|
||||
|
||||
subject.settings = {:custom_fields => {}}
|
||||
subject.set_custom_fields(hash, nil)
|
||||
expect(hash).to eq({:a => :b})
|
||||
end
|
||||
|
||||
it "merges fields from the settings' custom fields into the given hash prefixing them with custom_" do
|
||||
hash = {:a => :b}
|
||||
subject.settings = {:custom_fields => {:d => :e}}
|
||||
|
||||
subject.set_custom_fields(hash, nil)
|
||||
expect(hash).to eq({:a => :b, "custom_d" => :e})
|
||||
end
|
||||
|
||||
it "replaces non-word characters from custom field keys" do
|
||||
hash = {:a => :b}
|
||||
subject.settings = {:custom_fields => {:'%$#@d()' => :e}}
|
||||
|
||||
subject.set_custom_fields(hash, nil)
|
||||
expect(hash).to eq({:a => :b, "custom_____d__" => :e})
|
||||
end
|
||||
|
||||
it "merges fields from the applicable resource type too" do
|
||||
hash = {:a => :b}
|
||||
subject.settings = {:given_resource_type => {:custom_fields => {:'%$#@d()' => :e}}}
|
||||
|
||||
subject.set_custom_fields(hash, "given_resource_type")
|
||||
expect(hash).to eq({:a => :b, "custom_____d__" => :e})
|
||||
end
|
||||
end
|
||||
|
||||
describe "#selection_width" do
|
||||
it "returns selection width from settings for a resource type" do
|
||||
subject.settings = {editor_button: {:selection_width => 100}}
|
||||
expect(subject.selection_width('editor_button')).to eq 100
|
||||
end
|
||||
|
||||
it 'returns a default value if type is present in setting, but no selection width' do
|
||||
subject.settings = {editor_button: {}}
|
||||
expect(subject.selection_width('editor_button')).to eq 800
|
||||
end
|
||||
|
||||
it 'returns a default value if none set' do
|
||||
expect(subject.selection_width('editor_button')).to eq 800
|
||||
end
|
||||
end
|
||||
|
||||
describe "#selection_height" do
|
||||
it "returns selection height from settings for a resource type" do
|
||||
subject.settings = {editor_button: {:selection_height => 100}}
|
||||
expect(subject.selection_height('editor_button')).to eq 100
|
||||
end
|
||||
|
||||
it 'returns a default value if none set' do
|
||||
expect(subject.selection_height('editor_button')).to eq 400
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,132 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::LTIUser do
|
||||
it_behaves_like "an LTI context"
|
||||
|
||||
it_behaves_like "it has an attribute setter and getter for", :avatar_url
|
||||
it_behaves_like "it has an attribute setter and getter for", :email
|
||||
it_behaves_like "it has an attribute setter and getter for", :first_name
|
||||
it_behaves_like "it has an attribute setter and getter for", :last_name
|
||||
it_behaves_like "it has an attribute setter and getter for", :name
|
||||
it_behaves_like "it has an attribute setter and getter for", :login_id
|
||||
it_behaves_like "it has an attribute setter and getter for", :current_enrollments
|
||||
it_behaves_like "it has an attribute setter and getter for", :concluded_enrollments
|
||||
it_behaves_like "it has an attribute setter and getter for", :sis_user_id
|
||||
|
||||
it_behaves_like "it provides variable mapping", ".login_id", :login_id
|
||||
it_behaves_like "it provides variable mapping", ".enrollment_state", :enrollment_state
|
||||
it_behaves_like "it provides variable mapping", ".concluded_roles", :concluded_roles
|
||||
it_behaves_like "it provides variable mapping", ".full", :full_name
|
||||
it_behaves_like "it provides variable mapping", ".family", :family_name
|
||||
it_behaves_like "it provides variable mapping", ".given", :given_name
|
||||
it_behaves_like "it provides variable mapping", ".timezone", :timezone
|
||||
|
||||
let(:teacher_role_active) do
|
||||
BasicLtiOutbound::LTIRole.new.tap do |role|
|
||||
role.type = BasicLtiOutbound::LTIRole::INSTRUCTOR
|
||||
role.state = :active
|
||||
end
|
||||
end
|
||||
|
||||
let(:teacher_role_inactive) do
|
||||
BasicLtiOutbound::LTIRole.new.tap do |role|
|
||||
role.type = BasicLtiOutbound::LTIRole::INSTRUCTOR
|
||||
role.state = :inactive
|
||||
end
|
||||
end
|
||||
|
||||
let(:learner_role_active) do
|
||||
BasicLtiOutbound::LTIRole.new.tap do |role|
|
||||
role.type = BasicLtiOutbound::LTIRole::LEARNER
|
||||
role.state = :active
|
||||
end
|
||||
end
|
||||
|
||||
let(:learner_role_inactive) do
|
||||
BasicLtiOutbound::LTIRole.new.tap do |role|
|
||||
role.type = BasicLtiOutbound::LTIRole::LEARNER
|
||||
role.state = :inactive
|
||||
end
|
||||
end
|
||||
|
||||
describe "#current_role_types" do
|
||||
it "provides a string representation of current roles" do
|
||||
subject.tap do |user|
|
||||
user.current_enrollments = [teacher_role_active, learner_role_active]
|
||||
end
|
||||
|
||||
expect(subject.current_role_types).to eq("#{BasicLtiOutbound::LTIRole::INSTRUCTOR},#{BasicLtiOutbound::LTIRole::LEARNER}")
|
||||
end
|
||||
|
||||
it "defaults if no roles exist" do
|
||||
expect(subject.current_role_types).to eq(BasicLtiOutbound::LTIRole::NONE)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#concluded_role_types" do
|
||||
it "provides a string representation of concluded roles" do
|
||||
subject.tap do |user|
|
||||
user.concluded_enrollments = [teacher_role_active, learner_role_active]
|
||||
end
|
||||
|
||||
expect(subject.concluded_role_types).to eq("#{BasicLtiOutbound::LTIRole::INSTRUCTOR},#{BasicLtiOutbound::LTIRole::LEARNER}")
|
||||
end
|
||||
|
||||
it "defaults if no roles exist" do
|
||||
expect(subject.concluded_role_types).to eq(BasicLtiOutbound::LTIRole::NONE)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#enrollment_state" do
|
||||
it "returns 'active' if any current_enrollments are active" do
|
||||
subject.current_enrollments = [teacher_role_inactive, learner_role_active]
|
||||
|
||||
expect(subject.enrollment_state).to eq BasicLtiOutbound::LTIUser::ACTIVE_STATE
|
||||
end
|
||||
|
||||
it "returns 'inactive' if no current_enrollments are active" do
|
||||
subject.current_enrollments = [teacher_role_inactive, learner_role_inactive]
|
||||
|
||||
expect(subject.enrollment_state).to eq BasicLtiOutbound::LTIUser::INACTIVE_STATE
|
||||
end
|
||||
end
|
||||
|
||||
describe "constants" do
|
||||
it "provides enrollment state constants" do
|
||||
expect(BasicLtiOutbound::LTIUser::ACTIVE_STATE).to eq "active"
|
||||
expect(BasicLtiOutbound::LTIUser::INACTIVE_STATE).to eq "inactive"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#learner?" do
|
||||
it "returns false when current enrollments includes learner role" do
|
||||
subject.current_enrollments = [teacher_role_active, learner_role_active]
|
||||
|
||||
expect(subject.learner?).to eq true
|
||||
end
|
||||
|
||||
it "returns false when current enrollments do not include learner role" do
|
||||
subject.current_enrollments = [teacher_role_active]
|
||||
|
||||
expect(subject.learner?).to eq false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,478 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::ToolLaunch do
|
||||
before do
|
||||
@account = BasicLtiOutbound::LTIAccount.new.tap do |account|
|
||||
account.domain = "account_domain"
|
||||
account.lti_guid = "account_lti_guid"
|
||||
account.name = "account_name"
|
||||
account.id = "account_id"
|
||||
account.sis_source_id = "account_sis_source_id"
|
||||
end
|
||||
@root_account = BasicLtiOutbound::LTIAccount.new.tap do |account|
|
||||
account.domain = "root_account_domain"
|
||||
account.lti_guid = "root_account_lti_guid"
|
||||
account.name = "root_account_name"
|
||||
account.id = "root_account_id"
|
||||
account.sis_source_id = "root_account_sis_source_id"
|
||||
end
|
||||
@course = BasicLtiOutbound::LTICourse.new.tap do |course|
|
||||
course.root_account = @root_account
|
||||
course.opaque_identifier = "course_opaque_identifier"
|
||||
course.name = "course_name"
|
||||
course.id = "course_id"
|
||||
course.course_code = "course_code"
|
||||
course.sis_source_id = "course_sis_source_id"
|
||||
end
|
||||
@tool = BasicLtiOutbound::LTITool.new.tap do |tool|
|
||||
tool.name = "tool_name"
|
||||
tool.context = @course
|
||||
tool.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_PUBLIC
|
||||
end
|
||||
teacher_role = BasicLtiOutbound::LTIRole.new.tap do |role|
|
||||
role.type = BasicLtiOutbound::LTIRole::INSTRUCTOR
|
||||
role.state = :active
|
||||
end
|
||||
@user = BasicLtiOutbound::LTIUser.new.tap do |user|
|
||||
user.avatar_url = "avatar_url"
|
||||
user.current_enrollments = "current_enrollments"
|
||||
user.email = 'nobody@example.com'
|
||||
user.first_name = "first_name"
|
||||
user.id = "user_id"
|
||||
user.last_name = "last_name"
|
||||
user.login_id = "user_login_id"
|
||||
user.name = "user_name"
|
||||
user.opaque_identifier = "user_opaque_identifier"
|
||||
user.sis_user_id = "sis_user_id"
|
||||
user.current_enrollments = [teacher_role]
|
||||
user.concluded_enrollments = []
|
||||
user.root_account = @root_account
|
||||
end
|
||||
@assignment = BasicLtiOutbound::LTIAssignment.new.tap do |assignment|
|
||||
assignment.id = 'assignment_id'
|
||||
assignment.source_id = '123456'
|
||||
assignment.title = 'assignment1'
|
||||
assignment.points_possible = 100
|
||||
assignment.return_types = ['url', 'text']
|
||||
assignment.allowed_extensions = ['jpg', 'pdf']
|
||||
end
|
||||
@tool_launch = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @course,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.google.com',
|
||||
:outgoing_email_address => "outgoing_email_address")
|
||||
end
|
||||
|
||||
describe "#generate" do
|
||||
it "generates correct parameters" do
|
||||
hash = @tool_launch.generate
|
||||
|
||||
expect(hash['lti_message_type']).to eq 'basic-lti-launch-request'
|
||||
expect(hash['lti_version']).to eq 'LTI-1p0'
|
||||
expect(hash['resource_link_id']).to eq '123456'
|
||||
expect(hash['resource_link_title']).to eq "tool_name"
|
||||
expect(hash['user_id']).to eq "user_opaque_identifier"
|
||||
expect(hash['user_image']).to eq "avatar_url"
|
||||
expect(hash['roles']).to eq 'Instructor'
|
||||
expect(hash['context_id']).to eq "course_opaque_identifier"
|
||||
expect(hash['context_title']).to eq "course_name"
|
||||
expect(hash['context_label']).to eq "course_code"
|
||||
expect(hash['custom_canvas_user_id']).to eq "user_id"
|
||||
expect(hash['custom_canvas_user_login_id']).to eq "user_login_id"
|
||||
expect(hash['custom_canvas_course_id']).to eq "course_id"
|
||||
expect(hash['custom_canvas_api_domain']).to eq "root_account_domain"
|
||||
expect(hash['lis_course_offering_sourcedid']).to eq 'course_sis_source_id'
|
||||
expect(hash['lis_person_contact_email_primary']).to eq 'nobody@example.com'
|
||||
expect(hash['lis_person_name_full']).to eq 'user_name'
|
||||
expect(hash['lis_person_name_family']).to eq 'last_name'
|
||||
expect(hash['lis_person_name_given']).to eq 'first_name'
|
||||
expect(hash['lis_person_sourcedid']).to eq 'sis_user_id'
|
||||
expect(hash['launch_presentation_locale']).to eq "en" #was I18n.default_locale.to_s
|
||||
expect(hash['launch_presentation_document_target']).to eq 'iframe'
|
||||
expect(hash['launch_presentation_return_url']).to eq 'http://www.google.com'
|
||||
expect(hash['tool_consumer_instance_guid']).to eq "root_account_lti_guid"
|
||||
expect(hash['tool_consumer_instance_name']).to eq "root_account_name"
|
||||
expect(hash['tool_consumer_instance_contact_email']).to eq "outgoing_email_address"
|
||||
expect(hash['tool_consumer_info_product_family_code']).to eq 'canvas'
|
||||
expect(hash['tool_consumer_info_version']).to eq 'cloud'
|
||||
expect(hash['oauth_callback']).to eq 'about:blank'
|
||||
end
|
||||
|
||||
it "sets the locale if I18n.localizer exists" do
|
||||
I18n.localizer = lambda { :es }
|
||||
hash = @tool_launch.generate
|
||||
|
||||
expect(hash['launch_presentation_locale']).to eq 'es'
|
||||
I18n.localizer = lambda { :en }
|
||||
end
|
||||
|
||||
it "adds account info in launch data for account navigation" do
|
||||
hash = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @account,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.google.com').generate
|
||||
expect(hash['custom_canvas_account_id']).to eq "account_id"
|
||||
expect(hash['custom_canvas_account_sis_id']).to eq 'account_sis_source_id'
|
||||
expect(hash['custom_canvas_user_login_id']).to eq "user_login_id"
|
||||
end
|
||||
|
||||
it "adds account and user info in launch data for user profile launch" do
|
||||
hash = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @user,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.google.com').generate
|
||||
expect(hash['custom_canvas_account_id']).to eq "root_account_id"
|
||||
expect(hash['custom_canvas_account_sis_id']).to eq 'root_account_sis_source_id'
|
||||
expect(hash['lis_person_sourcedid']).to eq 'sis_user_id'
|
||||
expect(hash['custom_canvas_user_id']).to eq "user_id"
|
||||
expect(hash['tool_consumer_instance_guid']).to eq "root_account_lti_guid" #was hash['tool_consumer_instance_guid']).to eq sub_account.root_account.lti_guid
|
||||
end
|
||||
|
||||
it "includes URI query parameters" do
|
||||
hash = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com?paramater_a=value_a¶meter_b=value_b',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @course,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.google.com').generate
|
||||
expect(hash['paramater_a']).to eq 'value_a'
|
||||
expect(hash['parameter_b']).to eq 'value_b'
|
||||
end
|
||||
|
||||
it "does not allow overwriting other parameters from the URI query string" do
|
||||
hash = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com?user_id=ATTEMPT_TO_SET_DATA&oauth_callback=ATTEMPT_TO_SET_DATA',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @course,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.google.com').generate
|
||||
expect(hash['user_id']).to eq "user_opaque_identifier"
|
||||
expect(hash['oauth_callback']).to eq 'about:blank'
|
||||
end
|
||||
|
||||
it "includes custom fields" do
|
||||
@tool.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_ANYNOMOUS
|
||||
@tool.settings = {:custom_fields => {
|
||||
'custom_bob' => 'bob',
|
||||
'custom_fred' => 'fred',
|
||||
'john' => 'john',
|
||||
'@$TAA$#$#' => 123}}
|
||||
hash = @tool_launch.generate
|
||||
expect(hash.keys.select { |k| k.match(/^custom_/) }.sort).to eq(
|
||||
['custom___taa____', 'custom_bob', 'custom_canvas_enrollment_state', 'custom_fred', 'custom_john'])
|
||||
expect(hash['custom_bob']).to eql('bob')
|
||||
expect(hash['custom_fred']).to eql('fred')
|
||||
expect(hash['custom_john']).to eql('john')
|
||||
expect(hash['custom___taa____']).to eql('123')
|
||||
expect(hash).to_not have_key '@$TAA$#$#'
|
||||
expect(hash).to_not have_key 'john'
|
||||
end
|
||||
|
||||
it "does not include name and email if anonymous" do
|
||||
@tool.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_ANYNOMOUS
|
||||
hash = @tool_launch.generate
|
||||
expect(hash).to_not have_key 'lis_person_name_given'
|
||||
expect(hash).to_not have_key 'lis_person_name_family'
|
||||
expect(hash).to_not have_key 'lis_person_name_full'
|
||||
expect(hash).to_not have_key 'lis_person_contact_email_primary'
|
||||
end
|
||||
|
||||
it "includes name if name_only" do
|
||||
@tool.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_NAME_ONLY
|
||||
hash = @tool_launch.generate
|
||||
expect(hash['lis_person_name_given']).to eq "first_name"
|
||||
expect(hash['lis_person_name_family']).to eq "last_name"
|
||||
expect(hash['lis_person_name_full']).to eq "user_name"
|
||||
expect(hash['lis_person_contact_email_primary']).to be_nil
|
||||
end
|
||||
|
||||
it "includes email if email_only" do
|
||||
@tool.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_EMAIL_ONLY
|
||||
hash = @tool_launch.generate
|
||||
expect(hash['lis_person_name_given']).to eq nil
|
||||
expect(hash['lis_person_name_family']).to eq nil
|
||||
expect(hash['lis_person_name_full']).to eq nil
|
||||
expect(hash['lis_person_contact_email_primary']).to eq "nobody@example.com"
|
||||
end
|
||||
|
||||
it "includes email if public" do
|
||||
@tool.privacy_level = BasicLtiOutbound::LTITool::PRIVACY_LEVEL_PUBLIC
|
||||
hash = @tool_launch.generate
|
||||
expect(hash['lis_person_name_given']).to eq 'first_name'
|
||||
expect(hash['lis_person_name_family']).to eq "last_name"
|
||||
expect(hash['lis_person_name_full']).to eq "user_name"
|
||||
expect(hash['lis_person_contact_email_primary']).to eq "nobody@example.com"
|
||||
end
|
||||
|
||||
it "includes text if set" do
|
||||
@launch = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @course,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.yahoo.com')
|
||||
html = "<p>this has <a href='#'>a link</a></p>"
|
||||
@launch.has_selection_html!(html)
|
||||
hash = @launch.generate
|
||||
expect(hash['text']).to eq CGI::escape(html)
|
||||
end
|
||||
|
||||
it "gets the correct width and height based on resource type" do
|
||||
@tool.settings = {editor_button: {:selection_width => 1000, :selection_height => 300, :icon_url => 'www.example.com/icon', :url => 'www.example.com'}}
|
||||
hash = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @course,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.yahoo.com',
|
||||
:resource_type => 'editor_button').generate
|
||||
expect(hash['launch_presentation_width']).to eq '1000'
|
||||
expect(hash['launch_presentation_height']).to eq '300'
|
||||
end
|
||||
|
||||
describe "variable substitutions" do
|
||||
before do
|
||||
@substitutor = double('Substitutor', substitute!: 'something')
|
||||
BasicLtiOutbound::VariableSubstitutor.stub(:new).and_return(@substitutor)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.user" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.user', @user)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.context" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.context', @course)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.api" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.api', @root_account)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.assignment" do
|
||||
@tool_launch.for_homework_submission!(@assignment)
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.assignment', @assignment)
|
||||
end
|
||||
|
||||
it "does no substutue $Canvas.assignment if no assignment is given" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to_not have_received(:substitute!).with(anything, '$Canvas.assignment', anything)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.account with root account if context is not account" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.account', @course.root_account)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.account with account if context is account" do
|
||||
tool_launch = BasicLtiOutbound::ToolLaunch.new(:url => 'http://www.yahoo.com',
|
||||
:tool => @tool,
|
||||
:user => @user,
|
||||
:context => @account,
|
||||
:link_code => '123456',
|
||||
:return_url => 'http://www.google.com',
|
||||
:outgoing_email_address => "outgoing_email_address")
|
||||
tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.account', @account)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.membership" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.membership', @user)
|
||||
end
|
||||
|
||||
it "substitutes $Canvas.enrollment" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Canvas.enrollment', @user)
|
||||
end
|
||||
|
||||
it "substitutes $Person.name" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Person.name', @user)
|
||||
end
|
||||
|
||||
it "substitutes $Person.address" do
|
||||
@tool_launch.generate
|
||||
|
||||
expect(@substitutor).to have_received(:substitute!).with(anything, '$Person.address', @user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#for_assignment!" do
|
||||
it "includes assignment outcome service params for student" do
|
||||
student_role = BasicLtiOutbound::LTIRole.new.tap do |role|
|
||||
role.type = BasicLtiOutbound::LTIRole::LEARNER
|
||||
role.state = :active
|
||||
end
|
||||
@user.current_enrollments = [student_role]
|
||||
@tool_launch.for_assignment!(@assignment, '/my/test/url', '/my/other/test/url')
|
||||
|
||||
hash = @tool_launch.generate
|
||||
|
||||
expect(hash['lis_result_sourcedid']).to eq '123456'
|
||||
expect(hash['lis_outcome_service_url']).to eq "/my/test/url"
|
||||
expect(hash['ext_ims_lis_basic_outcome_url']).to eq "/my/other/test/url"
|
||||
expect(hash['ext_outcome_data_values_accepted']).to eq 'url,text'
|
||||
expect(hash['custom_canvas_assignment_title']).to eq "assignment1"
|
||||
expect(hash['custom_canvas_assignment_points_possible']).to eq "100"
|
||||
expect(hash['custom_canvas_assignment_id']).to eq "assignment_id"
|
||||
end
|
||||
|
||||
it "includes assignment outcome service params for teacher" do
|
||||
@tool_launch.for_assignment!(@assignment, '/my/test/url', '/my/other/test/url')
|
||||
|
||||
hash = @tool_launch.generate
|
||||
|
||||
expect(hash['lis_result_sourcedid']).to be_nil
|
||||
expect(hash['lis_outcome_service_url']).to eq "/my/test/url"
|
||||
expect(hash['ext_ims_lis_basic_outcome_url']).to eq "/my/other/test/url"
|
||||
expect(hash['ext_outcome_data_values_accepted']).to eq 'url,text'
|
||||
expect(hash['custom_canvas_assignment_title']).to eq "assignment1"
|
||||
expect(hash['custom_canvas_assignment_points_possible']).to eq "100"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#for_homework_submission!" do
|
||||
it "includes content keys if present" do
|
||||
@tool_launch.for_homework_submission!(@assignment)
|
||||
|
||||
hash = @tool_launch.generate
|
||||
|
||||
expect(hash['ext_content_return_types']).to eq 'url,text'
|
||||
expect(hash['ext_content_file_extensions']).to eq 'jpg,pdf'
|
||||
expect(hash['custom_canvas_assignment_id']).to eq 'assignment_id'
|
||||
end
|
||||
|
||||
it "excludes file_extensions if not present" do
|
||||
@assignment.allowed_extensions = nil
|
||||
@tool_launch.for_homework_submission!(@assignment)
|
||||
|
||||
hash = @tool_launch.generate
|
||||
|
||||
expect(hash['ext_content_file_extensions']).to eq nil
|
||||
end
|
||||
end
|
||||
|
||||
#TODO: do not test private methods
|
||||
describe ".generate_params" do
|
||||
def explicit_signature_settings(timestamp, nonce)
|
||||
BasicLtiOutbound::ToolLaunch.instance_variable_set(:"@timestamp", timestamp)
|
||||
BasicLtiOutbound::ToolLaunch.instance_variable_set(:"@nonce", nonce)
|
||||
end
|
||||
|
||||
it "generate a correct signature" do
|
||||
explicit_signature_settings('1251600739', 'c8350c0e47782d16d2fa48b2090c1d8f')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
:resource_link_id => '120988f929-274612',
|
||||
:user_id => '292832126',
|
||||
:roles => 'Instructor',
|
||||
:lis_person_name_full => 'Jane Q. Public',
|
||||
:lis_person_contact_email_primary => 'user@school.edu',
|
||||
:lis_person_sourced_id => 'school.edu:user',
|
||||
:context_id => '456434513',
|
||||
:context_title => 'Design of Personal Environments',
|
||||
:context_label => 'SI182',
|
||||
:lti_version => 'LTI-1p0',
|
||||
:lti_message_type => 'basic-lti-launch-request',
|
||||
:tool_consumer_instance_guid => 'lmsng.school.edu',
|
||||
:tool_consumer_instance_description => 'University of School (LMSng)',
|
||||
:basiclti_submit => 'Launch Endpoint with BasicLTI Data'
|
||||
}, 'http://dr-chuck.com/ims/php-simple/tool.php', '12345', 'secret')
|
||||
|
||||
expect(hash['oauth_signature']).to eql('TPFPK4u3NwmtLt0nDMP1G1zG30U=')
|
||||
end
|
||||
|
||||
it "generate a correct signature with URL query parameters" do
|
||||
explicit_signature_settings('1251600739', 'c8350c0e47782d16d2fa48b2090c1d8f')
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
:resource_link_id => '120988f929-274612',
|
||||
:user_id => '292832126',
|
||||
:roles => 'Instructor',
|
||||
:lis_person_name_full => 'Jane Q. Public',
|
||||
:lis_person_contact_email_primary => 'user@school.edu',
|
||||
:lis_person_sourced_id => 'school.edu:user',
|
||||
:context_id => '456434513',
|
||||
:context_title => 'Design of Personal Environments',
|
||||
:context_label => 'SI182',
|
||||
:lti_version => 'LTI-1p0',
|
||||
:lti_message_type => 'basic-lti-launch-request',
|
||||
:tool_consumer_instance_guid => 'lmsng.school.edu',
|
||||
:tool_consumer_instance_description => 'University of School (LMSng)',
|
||||
:basiclti_submit => 'Launch Endpoint with BasicLTI Data'
|
||||
}, 'http://dr-chuck.com/ims/php-simple/tool.php?a=1&b=2&c=3%20%26a', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('uF7LooyefQN5aocx7UlYQ4tQM5k=')
|
||||
expect(hash['c']).to eq "3 &a"
|
||||
end
|
||||
|
||||
it "generate a correct signature with a non-standard port" do
|
||||
#signatures generated using http://oauth.googlecode.com/svn/code/javascript/example/signature.html
|
||||
explicit_signature_settings('1251600739', 'c8350c0e47782d16d2fa48b2090c1d8f')
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'http://dr-chuck.com:123/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('ghEdPHwN4iJmsM3Nr4AndDx2Kx8=')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'http://dr-chuck.com/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('WoSpvCr2HEsLzao6Do0eukxwAsk=')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'http://dr-chuck.com:80/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('WoSpvCr2HEsLzao6Do0eukxwAsk=')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'http://dr-chuck.com:443/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('KqAV7eIS/+iWIDpvCyDfY8ZpmT4=')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'https://dr-chuck.com/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('wFRB/1ZXi/91dop6GwahfboWPvQ=')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'https://dr-chuck.com:443/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('wFRB/1ZXi/91dop6GwahfboWPvQ=')
|
||||
|
||||
hash = BasicLtiOutbound::ToolLaunch.send(:generate_params, {
|
||||
}, 'https://dr-chuck.com:80/ims/php-simple/tool.php', '12345', 'secret')
|
||||
expect(hash['oauth_signature']).to eql('X8Aq2HXSHnr6u/6z/G9zI5aDoR0=')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe BasicLtiOutbound::VariableSubstitutor do
|
||||
let(:account) {
|
||||
BasicLtiOutbound::LTIAccount.new.tap do |account|
|
||||
account.domain = 'my.domain'
|
||||
end
|
||||
}
|
||||
|
||||
it "substitutes variable" do
|
||||
params = {'domain' => '$Canvas.api.domain'}
|
||||
subject.substitute!(params, '$Canvas.api', account)
|
||||
expect(params).to eq({'domain' => 'my.domain'})
|
||||
end
|
||||
|
||||
it "does not replace invalid mappings" do
|
||||
params = {'domain' => '$Canvas.api.wrong'}
|
||||
subject.substitute!(params, '$Canvas.api', account)
|
||||
expect(params).to eq({'domain' => '$Canvas.api.wrong'})
|
||||
end
|
||||
|
||||
it "does not replace nil mappings" do
|
||||
account.domain = nil
|
||||
params = {'domain' => '$Canvas.api.domain'}
|
||||
subject.substitute!(params, '$Canvas.api', account)
|
||||
expect(params).to eq({'domain' => '$Canvas.api.domain'})
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
shared_examples_for "an LTI context" do
|
||||
it_behaves_like "it has an attribute setter and getter for", :root_account
|
||||
it_behaves_like "it has an attribute setter and getter for", :opaque_identifier
|
||||
it_behaves_like "it has an attribute setter and getter for", :id
|
||||
it_behaves_like "it has an attribute setter and getter for", :sis_source_id
|
||||
|
||||
it_behaves_like "it provides variable mapping", ".id", :id
|
||||
it_behaves_like "it provides variable mapping", ".sisSourceId", :sis_source_id
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
shared_examples_for "it has an attribute setter and getter for" do |attribute|
|
||||
it "the attribute '#{attribute}'" do
|
||||
obj = described_class.new
|
||||
expect(obj.send(attribute)).to eq nil
|
||||
obj.send("#{attribute}=", 10)
|
||||
expect(obj.send(attribute)).to eq 10
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
shared_examples_for "it provides variable mapping" do |key, method_name|
|
||||
it "maps #{key} to ##{method_name}" do
|
||||
object = described_class.new
|
||||
#object.send(:"#{method_name}=", 99)
|
||||
#object.variable_substitution_mapping(key).should == 99
|
||||
#expect(object.has_variable_mapping?(key)).to eq(true)
|
||||
expect(object).to receive(method_name)
|
||||
object.variable_substitution_mapping(key)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
# This file was generated by the `rspec --init` command. Conventionally, all
|
||||
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
||||
# Require this file using `require "spec_helper"` to ensure that it is only
|
||||
# loaded once.
|
||||
#
|
||||
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
||||
|
||||
require "basic_lti_outbound"
|
||||
|
||||
Dir.glob("#{File.dirname(__FILE__).gsub(/\\/, "/")}/shared_examples/*.rb").each { |file| require file }
|
||||
Dir.glob("#{File.dirname(__FILE__).gsub(/\\/, "/")}/support/*.rb").each { |file| require file }
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.filter_run :focus
|
||||
|
||||
# Run specs in random order to surface order dependencies. If you find an
|
||||
# order dependency and want to debug it, you can fix the order by providing
|
||||
# the seed, which is printed after each run.
|
||||
# --seed 1234
|
||||
config.order = 'random'
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
#Any including Application will need to perform the override in order for this gem to function as expected.
|
||||
|
||||
module I18n
|
||||
class << self
|
||||
attr_writer :localizer
|
||||
|
||||
# Public: If a localizer has been set, use it to set the locale and then
|
||||
# delete it.
|
||||
#
|
||||
# Returns nothing.
|
||||
def set_locale_with_localizer
|
||||
if @localizer
|
||||
self.locale = @localizer.call
|
||||
@localizer = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -8,6 +8,7 @@ module BasicLTI
|
|||
end
|
||||
|
||||
class Assignment < AbstractSubstitutor
|
||||
# $Canvas.assignment.id
|
||||
def id
|
||||
assignment.id if assignment
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue